You're viewing the legacy docs. They are deprecated as of May 18, 2016.
These docs are for version 2.5.1 and below of the Objective-C SDK. Go to our current docs, or see our iOS migration guide.

Objective-C and Swift iOS Guide

Offline Capabilities

Firebase apps work great offline and we have several features to make the experience even better. Enabling disk persistence allows your app to keep all of its state even after an app restart. We provide several tools for monitoring presence and connectivity state.

Disk Persistence

Firebase apps automatically handle temporary network interruptions for you. Cached data will still be available while offline and your writes will be resent when network connectivity is recovered. Enabling disk persistence allows our app to also keep all of its state even after an app restart. We can enable disk persistence with just one line of code.

[Firebase defaultConfig].persistenceEnabled = YES;
Firebase.defaultConfig().persistenceEnabled = true

With disk persistence enabled, our synced data and writes will be persisted to disk across app restarts and our app should work seamlessly in offline situations.

Persistence Behavior

By enabling persistence, any data that we sync while online will be persisted to disk and available offline, even when we restart the app. This means our app will work as it would online using the local data stored in the cache. Listener callbacks will continue to fire for local updates.

The Firebase client automatically keeps a queue of all write operations that are performed while our application is offline. When persistence is enabled, this queue will also be persisted to disk so all of our writes will be available when we restart the app. When the app regains connectivity, all of the operations will be sent to the server.

If our app uses Firebase Authentication, the client will persist the user's authentication token across restarts. If the auth token expires while our app is offline, the client will pause our write operations until we re-authenticate, else our writes might fail due to security rules.

Keeping Data Fresh

Firebase synchronizes and stores a local copy of the data for active listeners. In addition, you can keep specific locations in sync.

Firebase *scoresRef = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/scores"];
[scoresRef keepSynced: YES];
let scoresRef = Firebase(url:"https://dinosaur-facts.firebaseio.com/scores")
scoresRef.keepSynced(true)

The client will automatically download the data at these locations and keep it in sync even if the reference has no active listeners. You can turn synchronization back off with the following line of code.

[scoresRef keepSynced: NO];
scoresRef.keepSynced(false)

By default, 10MB of previously synced data will be cached. This should be enough for most applications. If the cache outgrows its configured size, Firebase will purge data that has been used least recently. Data that is kept in sync, will not be purged from the cache.

Querying Data Offline

Firebase stores data returned from a query for use when offline. For queries constructed while offline, Firebase continues to work for previously loaded data. If the requested data hasn't loaded, Firebase loads data from the local cache. When we come back online our data will load and reflect the query.

For example, here we have a piece of code in our app that queries for the last four items in our Firebase database of dinosaur scores.

Firebase *scoresRef = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/scores"];
[[[scoresRef queryOrderedByValue] queryLimitedToLast:4]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value);
}];
let scoresRef = Firebase(url:"https://dinosaur-facts.firebaseio.com/scores")
scoresRef.queryOrderedByValue().queryLimitedToLast(4).observeEventType(.ChildAdded, withBlock: { snapshot in
    println("The \(snapshot.key) dinosaur's score is \(snapshot.value)")
})

Now let's assume that the user loses connection, goes offline, and restarts the app. While still offline, we query for the last two items from the same location. This query will successfully return the last two items because we had loaded all four in the query above.

[[[scoresRef queryOrderedByValue] queryLimitedToLast:2]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"The %@ dinosaur's score is %@", snapshot.key, snapshot.value);
}];
scoresRef.queryOrderedByValue().queryLimitedToLast(2).observeEventType(.ChildAdded, withBlock: { snapshot in
    println("The \(snapshot.key) dinosaur's score is \(snapshot.value)")
})

In the above example, the firebase client raises 'child added' events for the highest scoring two dinosaurs, via our persisted cache. But it will not raise a 'value' event, since we've never done that query while online.

If we were to request the last six items while offline, we'd get 'child added' events for the four cached items straight away. When we come back online, the Firebase client will synchronize with the server and we'll get the final two 'child added' and the 'value' events.

Handling Transactions Offline

Any transactions that are performed while our app is offline, will be queued. Once the app regains network connectivity, the transactions will be sent to the server.

Transactions are not persisted across app restarts

Even with persistence enabled, transactions are not persisted across app restarts. So you cannot rely on transactions done offline being committed to your Firebase database. To provide the best user experience, your app should show that a transaction has not been saved into your Firebase database yet, or make sure your app remembers them manually and executes them again after an app restart.

Firebase has many features for dealing with offline scenarios and network connectivity. The rest of this guide applies to your app whether or not you have persistence enabled.

Managing Presence

In realtime applications it is often useful to detect when clients connect and disconnect. For example, we may want to mark a user as 'offline' when their client disconnects.

Firebase clients provide simple primitives that allow data to be written to the database when a client disconnects from the Firebase servers. These updates will occur whether the client disconnects cleanly or not, so we can rely on them to clean up data even if a connection is dropped or a client crashes. All write operations, including setting, updating, and removing, can be performed upon a disconnection.

Here is a simple example of writing data upon disconnection by using the onDisconnect primitive:

Firebase *presenceRef = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/disconnectmessage"];
// Write a string when this client loses connection
[presenceRef onDisconnectSetValue:@"I disconnected!"];
var presenceRef = Firebase(url:"https://<YOUR-FIREBASE-APP>.firebaseio.com/disconnectmessage")
// Write a string when this client loses connection
presenceRef.onDisconnectSetValue("I disconnected!")

How onDisconnect Works

When an onDisconnect() operation is established, it lives on the Firebase server. The server checks security to make sure the user can perform the write event requested, and informs the client if it is invalid. The server then monitors the connection. If at any point it times out, or is actively closed by the client, the server checks security a second time (to make sure the operation is still valid) and then invokes the event.

The client can use the callback on the write operation to ensure the onDisconnect was correctly attached:

[presenceRef onDisconnectRemoveValueWithCompletionBlock:^(NSError* error, FAUser* user) {
    if (error != nil) {
        NSLog(@"Could not establish onDisconnect event: %@", error);
    }
}];
ref.onDisconnectRemoveValueWithCompletionBlock({ error, user in
    if error != nil {
        println("Could not establish onDisconnect event: \(error)")
    }
})

An onDisconnect event can also be canceled by calling .cancel():

[presenceRef onDisconnectSetValue:@"I disconnected"];
// some time later when we change our minds
[presenceRef cancelDisconnectOperations];
presenceRef.onDisconnectSetValue("I disconnected")
// some time later when we change our minds
presenceRef.cancelDisconnectOperations()

Detecting Connection State

For many presence-related features, it is useful for a client to know when it is online or offline. Firebase clients provide a special location at /.info/connected which is updated every time the client's connection state changes. Here is an example:

Firebase *connectedRef = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/.info/connected"];
[connectedRef observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
  if([snapshot.value boolValue]) {
    NSLog(@"connected");
  } else {
    NSLog(@"not connected");
  }
}];
var connectedRef = Firebase(url:"https://<YOUR-FIREBASE-APP>.firebaseio.com/.info/connected")
connectedRef.observeEventType(.Value, withBlock: { snapshot in
    let connected = snapshot.value as? Bool
    if connected != nil && connected! {
        println("Connected")
    } else {
        println("Not connected")
    }
})

/.info/connected is a boolean values which is not synchronized between clients because the values are dependent on the state of the client. In other words, if one client reads /.info/connected as false, this is no guarantee that a separate client will also read false.

Handling Latency

Server Timestamps

Firebase servers provide a mechanism to insert timestamps generated on the server as data. This feature, combined with onDisconnect, provides an easy way to reliably make note of the time at which a client disconnected:

Firebase *userLastOnlineRef = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/users/joe/lastOnline"];
[userLastOnlineRef onDisconnectSetValue:kFirebaseServerValueTimestamp];
var userLastOnlineRef = Firebase(url:"https://<YOUR-FIREBASE-APP>.firebaseio.com/users/joe/lastOnline")
userLastOnlineRef.onDisconnectSetValue([".sv": "timestamp"])

Clock Skew

While Firebase.ServerValue.TIMESTAMP is much more accurate, and preferable for most read/write ops, it can occasionally be useful to estimate the clients clock skew with respect to Firebase's servers. We can attach a callback to the location /.info/serverTimeOffset to obtain the value, in milliseconds, that Firebase clients will add to the local reported time (epoch time in milliseconds) to estimate the server time. Note that this offset's accuracy can be affected by networking latency, and so is useful primarily for discovering large (> 1 second) discrepancies in clock time.

Firebase *offsetRef = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/.info/serverTimeOffset"];
[offsetRef observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
  offset = [(NSNumber *)snapshot.value doubleValue];
  double estimatedServerTimeMs = [[NSDate date] timeIntervalSince1970] * 1000.0 + offset;
}];
var offsetRef = Firebase(url:"https://<YOUR-FIREBASE-APP>.firebaseio.com/.info/serverTimeOffset")
offsetRef.observeEventType(.Value, withBlock: { snapshot in
    var offset = snapshot.value as? Double
    var estimatedServerTimeMs = NSDate().timeIntervalSince1970 * 1000.0 + offset!
})

Sample Presence App

By combining disconnect operations with connection state monitoring and server timestamps, we can build a user presence system. In this system, each user stores data at a database location to indicate whether or not a client is online. Clients set this location to true when they come online and a timestamp when they disconnect. This timestamp indicates the last time the given user was online.

Note that the disconnect operations should be queued before a user is marked online, to avoid any race conditions in case the client's network connection is lost before both commands can be sent to the server.

Here is a simple user presence system:

// since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
Firebase *myConnectionsRef = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/users/joe/connections"];

// stores the timestamp of my last disconnect (the last time I was seen online)
Firebase *lastOnlineRef = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/users/joe/lastOnline"];

Firebase *connectedRef = [[Firebase alloc] initWithUrl:@"https://<YOUR-FIREBASE-APP>.firebaseio.com/.info/connected"];
[connectedRef observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
  if([snapshot.value boolValue]) {
    // connection established (or I've reconnected after a loss of connection)

    // add this device to my connections list
    // this value could contain info about the device or a timestamp instead of just true
    Firebase *con = [myConnectionsRef childByAutoId];
    [con setValue:@YES];

    // when this device disconnects, remove it
    [con onDisconnectRemoveValue]

    // when I disconnect, update the last time I was seen online
    [lastOnlineRef onDisconnectSetValue:kFirebaseServerValueTimestamp];
  }
}];
// since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
var myConnectionsRef = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/users/joe/connections")

// stores the timestamp of my last disconnect (the last time I was seen online)
var lastOnlineRef = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/users/joe/lastOnline")

var connectedRef = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/.info/connected")

connectedRef.observeEventType(.Value, withBlock: { snapshot in
    let connected = snapshot.value as? Bool
    if connected != nil && connected! {
        // connection established (or I've reconnected after a loss of connection)

        // add this device to my connections list
        // this value could contain info about the device or a timestamp instead of just true
        var con = myConnectionsRef.childByAutoId()
        con.setValue("YES")

        // when this device disconnects, remove it
        con.onDisconnectRemoveValue()

        // when I disconnect, update the last time I was seen online
        lastOnlineRef.onDisconnectSetValue([".sv": "timestamp"])
    }
})
  1. 1

    Next

    Installation & Setup

  2. 2

    Next

    Understanding Data

  3. 3

    Next

    Saving Data

  4. 4

    Next

    Retrieving Data

  5. 5

    Next

    Structuring Data

  6. 6

    Next

    Understanding Security

  7. 7

    Next

    User Authentication

  8. 8

    Next

    Offline Capabilities