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

Saving Data

In this section we'll cover the four methods for writing data to your Firebase database: setValue, updateChildValues, childByAutoId, and our runTransactionBlock feature.

Ways to Save Data

setValue Write data to a defined path, like messages/users/<username>
updateChildValues Update the value of existing data in your database
childByAutoId Generate a location with an unique ID in the database. Every time you call childByAutoId your database generates a unique ID, like messages/users/<unique-user-id>/<username>
runTransactionBlock Use our transactions feature when working with complex data that could be affected by concurrent updates

Using setValue to Write Data

The basic database write operation is setValue, which saves new data to the specified database reference, replacing any existing data at that path. It makes sense to use setValue here instead of push since we already have the key and don't need to create one. To understand setValue, we'll build a simple blogging app. The data for our app will be stored at this database reference:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://docs-examples.firebaseio.com/web/saving-data/fireblog"];
var ref = Firebase(url: "https://docs-examples.firebaseio.com/web/saving-data/fireblog")

Let's start by saving some user data. We'll identify each user in our database by a unique username, and we'll also store their full name and date of birth. Since each user will have a unique username, it makes sense to use setValue here.

First, let's create a dictionary of users we want to store in the database. We'll create a reference to the location of our user data and call setValue to save a user object with the user's username, full name, and birthday. We can pass setValue our dictionary.

  NSDictionary *alanisawesome = @{
      @"full_name" : @"Alan Turing",
      @"date_of_birth": @"June 23, 1912"
  };
  NSDictionary *gracehop = @{
      @"full_name" : @"Grace Hopper",
      @"date_of_birth": @"December 9, 1906"
  };
  Firebase *usersRef = [ref childByAppendingPath: @"users"];
  NSDictionary *users = @{
      @"alanisawesome": alanisawesome,
      @"gracehop": gracehop
  };
  [usersRef setValue: users];
  var alanisawesome = ["full_name": "Alan Turing", "date_of_birth": "June 23, 1912"]
  var gracehop = ["full_name": "Grace Hopper", "date_of_birth": "December 9, 1906"]

  var usersRef = ref.childByAppendingPath("users")

  var users = ["alanisawesome": alanisawesome, "gracehop": gracehop]
  usersRef.setValue(users)

When the dictionary is saved to the database, the properties are automatically mapped to child locations in a nested fashion. Now if we navigate to the URL https://docs-examples.firebaseio.com/web/saving-data/fireblog/users/alanisawesome/full_name, we'll see the value "Alan Turing". You can also save data directly to a child location:

Firebase *alanRef = [usersRef childByAppendingPath: @"alanisawesome"];
[alanRef setValue: alanisawesome];

Firebase *hopperRef = [usersRef childByAppendingPath: @"gracehop"];
[hopperRef setValue: gracehop];
usersRef.childByAppendingPath("alanisawesome").setValue(alanisawesome)
usersRef.childByAppendingPath("gracehop").setValue(gracehop)

The above two examples - writing both values at the same time as a dictionary and writing them separately to child locations - will result in the same data being saved to your database:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
   },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper"
    }
  }
}

The first example will only trigger one event on clients that are watching the data, whereas the second example will trigger two. It is important to note that if data already existed at usersRef, the first method would overwrite it, but the second method would only modify the value of each separate child node while leaving other children of usersRef unchanged.

Using setValue will overwrite the data at the specified location, including any child nodes.

Updating Saved Data

If you want to write to multiple children of a database location at the same time without overwriting other existing data, you can use the updateChildValues method as shown below:

Firebase *hopperRef = [usersRef childByAppendingPath: @"gracehop"];

NSDictionary *nickname = @{
    @"nickname": @"Amazing Grace",
};

[hopperRef updateChildValues: nickname];

var hopperRef = usersRef.childByAppendingPath("gracehop")
var nickname = ["nickname": "Amazing Grace"]

hopperRef.updateChildValues(nickname)

This will update Grace's data to include her nickname. If we had used setValue here instead of updateChildValues, it would have deleted both full_name and date_of_birth from our hopperRef.

Firebase also supports multi-path updates. This means that updateChildValues can now update values at multiple locations in your Firebase database at the same time, a powerful feature which allows helps you denormalize your data. Using multi-path updates, we can add nicknames to both Grace and Alan at the same time:

[usersRef updateChildValues: @{
  @"alanisawesome/nickname": @"Alan The Machine",
  @"gracehop/nickname": @"Amazing Grace"
}];
usersRef.updateChildValues([
  "alanisawesome/nickname": "Alan The Machine",
  "gracehop/nickname": "Amazing Grace"
])

After this update, both Alan and Grace have had their nicknames added:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

Note that trying to update objects by writing objects with the paths included will result in different behavior. Let's take a look at what happens if we instead try to update Grace and Alan this way:

[usersRef updateChildValues: @{
  @"alanisawesome": @{
    @"nickname": @"Alan The Machine"
  },
  @"gracehop": @{
    @"nickname": @"Amazing Grace"
  }
}];
usersRef.updateChildValues([
  "alanisawesome": [
    "nickname": "Alan The Machine"
  ],
  "gracehop": [
    "nickname": "Amazing Grace"
  ]
])

This results in different behavior, namely overwriting the entire /users node:

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}
Updating Nested Data

Given a single key path like alanisawesome, updateChildValues only updates data at the first child level, and any data passed in beyond the first child level is a treated as a setValue operation. Multi-path behavior allows longer paths (like alanisawesome/nickname) to be used without overwriting data. This is why the first example differs from the second example.

Adding a Completion Block

If you'd like to know when your data has been committed, you can add a completion block. Both setValue and updateChildValues take an optional completion block that is called when the write has been committed to the database. This listener can be useful for keeping track of which data has been saved and which data is still being synchronized. If the call was unsuccessful for some reason, the listener will be passed an error object indicating why the failure occurred.

[ref setValue:@"I'm writing data" withCompletionBlock:^(NSError *error, Firebase *ref) {
    if (error) {
        NSLog(@"Data could not be saved.");
    } else {
        NSLog(@"Data saved successfully.");
    }
}];
ref.setValue("I'm writing data", withCompletionBlock: {
    (error:NSError?, ref:Firebase!) in
    if (error != nil) {
      println("Data could not be saved.")
    } else {
      println("Data saved successfully!")
    }
})

Saving Lists with childByAutoId

When creating lists of data, it is important to keep in mind the multi-user nature of most applications and adjust your list structure accordingly. Expanding on our example above, let's add blog posts to our app. Your first instinct might be to use setValue to store children with auto-incrementing integer indexes, like the following:

ANTIPATTERN: This is not a recommended practice
// NOT RECOMMENDED - use childByAutoId!
{
  "posts": {
    "0": {
      "author": "gracehop",
      "title": "Announcing COBOL, a New Programming Language"
    },
    "1": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

If a user adds a new post it would be stored as /posts/2. This would work if only a single author were adding posts, but in our collaborative blogging application many users may add posts at the same time. If two authors write to /posts/2 simultaneously, then one of the posts would be deleted by the other.

To solve this, the Firebase iOS client library provides a childByAutoId function that generates a unique ID every time a new child is added to the specified database reference. By using unique child keys for each new element in the list, several clients can add children to the same location at the same time without worrying about write conflicts. The unique ID generated by childByAutoId is based on a timestamp, so list items will automatically be ordered chronologically.

We can add posts to our blogging app with chronological, unique IDs by doing the following:

Firebase *postRef = [ref childByAppendingPath: @"posts"];
NSDictionary *post1 = @{
    @"author": @"gracehop",
    @"title": @"Announcing COBOL, a New Programming Language"
};
Firebase *post1Ref = [postRef childByAutoId];
[post1Ref setValue: post1];

NSDictionary *post2 = @{
    @"author": @"alanisawesome",
    @"title": @"The Turing Machine"
};
Firebase *post2Ref = [ref childByAutoId];
[post2Ref setValue: post2];
let postRef = ref.childByAppendingPath("posts")
let post1 = ["author": "gracehop", "title": "Announcing COBOL, a New Programming Language"]
let post1Ref = postRef.childByAutoId()
post1Ref.setValue(post1)

let post2 = ["author": "alanisawesome", "title": "The Turing Machine"]
let post2Ref = postRef.childByAutoId()
post2Ref.setValue(post2)

Because we used childByAutoId, Firebase generated a timestamp-based, unique ID for each blog post and no write conflicts will occur if multiple users create a blog post at the same time. Our data in the Firebase database now looks like this:

{
  "posts": {
    "-JRHTHaIs-jNPLXOQivY": {
      "author": "gracehop",
      "title": "Announcing COBOL, a New Programming Language"
     },
    "-JRHTHaKuITFIhnj02kE": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

Getting the Unique ID Generated by childByAutoId

Calling childByAutoId will return a reference to the new data path, which you can use to get the value of its ID or set data to it.

NSDictionary *post1 = @{
    @"author": @"gracehop",
    @"title": @"Announcing COBOL, a New Programming Language"
};
Firebase *post1Ref = [ref childByAutoId];
[post1Ref setValue: post1];

NSString *postId = post1Ref.key;
var post1 = ["author": "gracehop", "title": "Announcing COBOL, a New Programming Language"]
var post1Ref = ref.childByAutoId()
post1Ref.setValue(post1)

var postId = post1Ref.key

As you can see, calling key on our childByAutoId reference gives us the value of the unique ID.

Interested in learning more about how push IDs are generated? Check out this blog post.

In the next article on Retrieving Data, we'll learn how to read this data from a Firebase database.

Saving Transactional Data

When working with complex data that could be corrupted by concurrent modifications, such as incremental counters, we provide a transaction operation. You give this operation two arguments: an update function and an optional completion block. The update function takes the current state of the data as an argument and will return the new desired state you would like to write. For example, if we wanted to increment the number of upvotes on a specific blog post, we would write a transaction like the following:

Firebase *upvotesRef = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes"];

[upvotesRef runTransactionBlock:^FTransactionResult *(FMutableData *currentData) {
    NSNumber *value = currentData.value;
    if (currentData.value == [NSNull null]) {
        value = 0;
    }
    [currentData setValue:[NSNumber numberWithInt:(1 + [value intValue])]];
    return [FTransactionResult successWithValue:currentData];
}];
var upvotesRef = Firebase(url: "https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes")

upvotesRef.runTransactionBlock({
    (currentData:FMutableData!) in
    var value = currentData.value as? Int
    if !value {
        value = 0
    }
    currentData.value = value! + 1
    return FTransactionResult.successWithValue(currentData)
})

We use currentValue.value || 0 to see if the counter is null or hasn't been incremented yet, since transactions can be called with null if no default value was written.

If the above code had been run without a transaction function and multiple clients attempted to increment it simultaneously, they would both write 1 as the new value, resulting in one increment instead of two.

Transaction Function is Called Multiple Times

runTransactionBlock() will be called multiple times and must be able to handle null data. Even if there is existing data in your database it may not be locally cached when the transaction function is run.

Details on transactions can be found in our API docs.

Network Connectivity and Offline Writes

If a client loses network connection, your app will continue functioning correctly regardless of network state.

Every client connected to a Firebase database maintains its own internal version of any active data. When data is written, it is written to this local version of the database. The Firebase client then synchronizes that data with the Firebase servers and with other clients on a 'best-effort' basis.

As a result, all writes to database will trigger local events immediately, before any data has even been written to the server. This means you can write your application by using Firebase as your data model, and your app will remain responsive regardless of network latency or Internet connectivity.

If a client loses network connection, your app will continue functioning correctly regardless of network state. Once connectivity is reestablished, you'll receive the appropriate set of events so that your client "catches up" with the current server state, without you having to write any custom code.

Securing Your Data

Firebase has a security language that lets you define which users have read and write access to different nodes of your data. You can read more about it in Securing Your App.

  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