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

Retrieving Data

Firebase data is retrieved by attaching an asynchronous listener to a database reference. The listener will be triggered once for the initial state of the data and again anytime the data changes. This document will cover the basics of retrieving data, how data is ordered, and how to perform simple queries on data in your Firebase database.

Getting Started

Let's revisit our blogging example from the previous article to understand how we read data from a Firebase database. Recall that the blog posts in our example app are stored at the Firebase URL https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts. To read our post data, we can do the following:

// Get a reference to our posts
Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];

// Attach a block to read the data at our posts reference
[ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"%@", snapshot.value);
} withCancelBlock:^(NSError *error) {
    NSLog(@"%@", error.description);
}];
// Get a reference to our posts
var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")

// Attach a closure to read the data at our posts reference
ref.observeEventType(.Value, withBlock: { snapshot in
    println(snapshot.value)
}, withCancelBlock: { error in
    println(error.description)
})

If we run this code, we'll see an object containing all of our posts logged to the console. This block will be called anytime new data is added to our database reference, and we don't need to write any extra code to make this happen.

The callback function receives an FDataSnapshot, which is a snapshot of the data. A snapshot is a picture of the data at a particular database location at a single point in time. Accessing the value property on a snapshot returns the data as an id. If no data exists at the location, the snapshot's value will be NSNull.

Notice that we need to specify the FEventType. We use the Value event type in our example above, which is FEventTypeValue in Objective-C and FEventType.Value (or just .Value) in Swift. This reads the entire contents of a Firebase database. Value is one of the five different event types of the FEventType enum listed below that we can use to read data.

Read Event Types

Value

The Value event is used to read a static snapshot of the contents at a given path, as they existed at the time of the read event. It is triggered once with the initial data and again every time the data changes. The event callback is passed a snapshot containing all data at that location, including child data. In our code example above, observing the Value event type returned all of the blog posts in our app. Everytime a new blog post is added, the callback function will return all of the posts. This is denoted by FEventTypeValue in Objective-C and FEventType.Value in Swift.

Child Added

The ChildAdded event is typically used when retrieving a list of items in your Firebase database. Unlike Value which returns the entire contents of the location, ChildAdded is triggered once for each existing child and then again every time a new child is added to the specified path. The event callback is passed a snapshot containing the new child's data. In Objective-C, this is FeventTypeChildAdded and in Swift, this is FEventType.ChildAdded.

If we wanted to retrieve only the data on each new post added to our blogging app, we could use ChildAdded:

// Get a reference to our posts
Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];

// Retrieve new posts as they are added to the database
[ref observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
  NSLog(@"%@", snapshot.value[@"author"]);
  NSLog(@"%@", snapshot.value[@"title"]);
}];
// Get a reference to our posts
var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")

// Retrieve new posts as they are added to your database
ref.observeEventType(.ChildAdded, withBlock: { snapshot in
    println(snapshot.value.objectForKey("author"))
    println(snapshot.value.objectForKey("title"))
})

In this example the snapshot will contain an object with an individual blog post. We can access the post's author and title properties by getting the value at the "author" and "title" keys.

Child Changed

The ChildChanged event is triggered any time a child node is modified. This includes any modifications to descendants of the child node. It is typically used in conjunction with ChildAdded and ChildRemoved to respond to changes to a list of items. The snapshot passed to the event callback contains the updated data for the child. This is specified as FEventTypeChildChanged in Objective-C and FEventType.ChildChanged in Swift.

We can use the ChildChanged event to read updated data on blog posts when they are edited:

// Get a reference to our posts
Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];

// Get the data on a post that has changed
[ref observeEventType:FEventTypeChildChanged withBlock:^(FDataSnapshot *snapshot) {
  NSLog(@"The updated post title is %@", snapshot.value[@"title"]);
}];
// Get a reference to our posts
var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")

// Get the data on a post that has changed
ref.observeEventType(.ChildChanged, withBlock: { snapshot in
    let title = snapshot.value.objectForKey("title") as? String
    println("The updated post title is \(title)")
})

Child Removed

The ChildRemoved event is triggered when an immediate child is removed. It is typically used in conjunction with ChildAdded and ChildChanged events. The snapshot passed to the event callback contains the data for the removed child. To denote this in Objective-C, we use FEventTypeChildRemoved, and in Swift, we use FEventType.ChildRemoved.

In our blog example, we'll use ChildRemoved to log a notification about the deleted post to the console:

// Get a reference to our posts
Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];

// Get the data on a post that has been removed
[ref observeEventType:FEventTypeChildRemoved withBlock:^(FDataSnapshot *snapshot) {
  NSLog(@"The blog post titled %@ has been deleted", snapshot.value[@"title"]);
}];
// Get a reference to our posts
var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")

// Get the data on a post that has been removed
ref.observeEventType(.ChildRemoved, withBlock: { snapshot in
    let title = snapshot.value.objectForKey("title") as? String
    println("The blog post titled \(title)")
})

Child Moved

The ChildMoved event is used when working with ordered data, which is covered in the next section.

Event Guarantees

Firebase makes several important guarantees regarding events:

Firebase Event Guarantees
Events will always be triggered when local state changes.
Events will always eventually reflect the correct state of the data, even in cases where local operations or timing cause temporary differences, such as in the temporary loss of network connection.
Writes from a single client will always be written to the server and broadcast out to other users in-order.
Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken.

Since value events are always triggered last, the following example will always work:

// Get a reference to our posts
Firebase *ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts"];
__block NSInteger count = 0;

[ref observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
    count++;
    NSLog(@"added -> %@", snapshot.value);
}];

// snapshot.childrenCount will always equal count since snapshot.value will include every FEventTypeChildAdded event
// triggered before this point.
[ref observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"initial data loaded! %d", count == snapshot.childrenCount);
}];
// Get a reference to our posts
var ref = Firebase(url:"https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts")
var count:UInt = 0

// Retrieve new posts as they are added to the database
ref.observeEventType(.ChildAdded, withBlock: { snapshot in
    count++
    println("added -> \(snapshot.value)")
})

// snapshot.childrenCount will always equal count since snapshot.value will include every FEventTypeChildAdded event
// triggered before this point.
ref.observeEventType(.Value, withBlock: { snapshot in
  println("initial data loaded! \(count == snapshot.childrenCount)")
})

Detaching Blocks

Just because we leave a ViewController doesn't mean our observers stop syncing data. If an observer isn't properly removed it will continue to sync data to local memory. When an observer is no longer needed it should be removed by passing the associated FirebaseHandle to removeObserverWithHandle.

When adding a callback block to a reference, a FirebaseHandle will be returned. These handles can be used to remove the callback block as follows:

FirebaseHandle handle = [ref observeEventType:FEventTypeValue withBlock:^(FDatasnapshot* snapshot) {
    NSLog(@"Snapshot value: %@", snapshot.value)
}];

[ref removeObserverWithHandle:handle];
var handle = ref.observeEventType(.Value, withBlock: { snapshot in
    println("Snapshot value: \(snapshot.value)")
})

ref.removeObserverWithHandle(handle)

If we would like to remove all callbacks at a location, we can do the following:

[ref removeAllObservers];
ref.removeAllObservers()

Reading Data Once

In some cases it may be useful for a callback to be called once and then immediately removed. We've created a helper function to make this easy:

[ref observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {
    // do some stuff once
}];
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
    // do some stuff once
})

Checking for NSNull

If no data exists at a the specified location, the FDataSnapshot will return an NSNull. We can check for NSNull with the following syntax:

[ref observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) {

  if (snapshot.value == [NSNull null]) {
    // The value is null
  }

}];
ref.observeEventType(.Value, withBlock: { snap in

  if snap.value is NSNull {
    // The value is null
  }

})

Querying Data

We can selectively retrieve data based on various conditions using queries. To construct a query, you start by specifying how you want your data to be ordered using one of the ordering functions: queryOrderedByChild:, queryOrderedByKey:, queryOrderedByValue, or queryOrderedByPriority:. You can then combine these with five other methods to conduct complex queries: queryLimitedToFirst:, queryLimitedToLast:, queryStartingAtValue:, queryEndingAtValue:, and queryEqualToValue:.

Since all of us at Firebase think dinosaurs are pretty cool, we'll use this database of dinosaur facts to demonstrate how you can query data. Here's a snippet of the dinosaur data:

{
  "lambeosaurus": {
    "height" : 2.1,
    "length" : 12.5,
    "weight": 5000
  },
  "stegosaurus": {
    "height" : 4,
    "length" : 9,
    "weight" : 2500
  }
}

We can order data in three ways: by child key, by key name, or by priority. A basic database query starts with one of these ordering functions, each of which are explained below.

Ordering using a child key

We can order nodes by a common child key by passing that key to queryOrderedByChild:. For example, to read all dinosaurs ordered by height, we can do the following:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[ref queryOrderedByChild:@"height"]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@ was %@ meters tall", snapshot.key, snapshot.value[@"height"]);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByChild("height").observeEventType(.ChildAdded, withBlock: { snapshot in
    if let height = snapshot.value["height"] as? Double {
        println("\(snapshot.key) was \(height) meters tall")
    }
})

Any node which does not have the child key we're querying on will be sorted with a value of null, meaning it will come first in the ordering. For details on how data is ordered, see the How Data is Ordered section.

Queries can also be ordered by deeply nested children, rather than only children one level down. This is useful if you have deeply nested data like this:

{
  "lambeosaurus": {
    "dimensions": {
      "height" : 2.1,
      "length" : 12.5,
      "weight": 5000
    }
  },
  "stegosaurus": {
    "dimensions": {
      "height" : 4,
      "length" : 9,
      "weight" : 2500
    }
  }
}

To query the height now, we use the full path to the object rather than a single key:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[ref queryOrderedByChild:@"dimensions/height"]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@ was %@ meters tall", snapshot.key, snapshot.value[@"height"]);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByChild("dimensions/height").observeEventType(.ChildAdded, withBlock: { snapshot in
    if let height = snapshot.value["height"] as? Double {
        println("\(snapshot.key) was \(height) meters tall")
    }
})

Queries can only order by one key at a time. Calling queryOrderedByChild: multiple times on the same query throws an error.

Using Indexes For Improved Performance

If you want to use queryOrderedByChild: on a production app, you should define the keys you will be indexing on via the .indexOn rule in your Security and Firebase Rules. While Firebase allows you to create these queries ad-hoc on the client, you will see greatly improved performance when using .indexOn. Read the documentation on the .indexOn rule for more information.

Ordering by key name

We can also order nodes by their keys using the queryOrderedByKey: method. The following example reads all dinosaurs in alphabetical order:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[ref queryOrderedByKey]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@ was %@", snapshot.key, snapshot.value[@"height"]);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByKey().observeEventType(.ChildAdded, withBlock: { snapshot in
    if let height = snapshot.value["height"] as? Double {
        println("\(snapshot.key) was \(height)")
    }
})

Ordering by value

We can order nodes by the value of their child keys using the queryOrderedByValue method. Let's say the dinosaurs are having a dino sports competition and we're keeping track of their scores in the following format:

{
  "scores": {
    "bruhathkayosaurus" : 55,
    "lambeosaurus" : 21,
    "linhenykus" : 80,
    "pterodactyl" : 93,
    "stegosaurus" : 5,
    "triceratops" : 22
  }
}

To sort the dinosaurs by their score, we could construct the following query:

Firebase *scoresRef = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/scores"];
[[scoresRef queryOrderedByValue] 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().observeEventType(.ChildAdded, withBlock: { snapshot in
    if let score = snapshot.value as? Int {
        println("The \(snapshot.key) dinosaur's score is \(score)")
    }
})

See the How Data is Ordered section for an explanation on how null, boolean, string, and object values are sorted when using queryOrderedByValue.

Indexing on Values for Improved Performance

If you want to use orderByValue() in a production app, you should add .value to your rules at the appropriate index. Read the documentation on the .indexOn rule for more information.

Ordering by priority

We can explicitly order nodes by priority by calling the queryOrderedByPriority: method. Details on priorities can be found in the API reference.

Complex Queries

Now that we've specified how your data will be ordered, we can use the limit or range methods described below to construct more complex queries.

Limit Queries

The queryLimitedToFirst: and queryLimitedToLast: queries are used to set a maximum number of children to be synced for a given callback. If we set a limit of 100, we will initially only receive up to 100 ChildAdded events. If we have fewer than 100 messages stored in our database, a ChildAdded event will fire for each message. However, if we have over 100 messages, we will only receive a ChildAdded event for 100 of those messages. These will be the first 100 ordered messages if we are using queryLimitedToFirst: or the last 100 ordered messages if we are using queryLimitedToLast:. As items change, we will receive ChildAdded events for items that enter the query and ChildRemoved events for items that leave it, so that the total number stays at 100.

Using our dinosaur facts database and queryOrderedByChild:, we can find the two heaviest dinosaurs:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[ref queryOrderedByChild:@"weight"] queryLimitedToLast:2] observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByChild("weight").queryLimitedToLast(2)
   .observeEventType(.ChildAdded, withBlock: { snapshot in

    println(snapshot.key)
})

Our ChildAdded callback will be triggered exactly two times, unless there are less than two dinosaurs stored in the database. It will also get fired for every new, heavier dinosaur that gets added to the data set.

Similarly, we can find the two shortest dinosaurs by using the queryLimitedToFirst: method:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[ref queryOrderedByChild:@"height"] queryLimitedToFirst:2]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByChild("height").queryLimitedToFirst(2)
   .observeEventType(.ChildAdded, withBlock: { snapshot in

    println(snapshot.key)
})

Our ChildAdded callback will be triggered exactly two times, unless there are less than two dinosaurs stored in the database. It will also get fired again if one of the first two dinosaurs is removed from the data set, as a new dinosaur will now be the second shortest.

We can also conduct limit queries with queryOrderedByValue. If we want to create a leaderboard with the top 3 highest scoring dino sports dinosaurs, we could do the following:

Firebase *scoresRef = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/scores"];
[[[scoresRef queryOrderedByValue] queryLimitedToLast:3]
    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(3).observeEventType(.ChildAdded, withBlock: { snapshot in

    println("The \(snapshot.key) dinosaur's score is \(snapshot.value)")
})

Range Queries

Using queryStartingAtValue:, queryEndingAtValue:, and queryEqualToValue: allows us to choose arbitrary starting and ending points for our queries. For example, if we wanted to find all dinosaurs that are at least three meters tall, we can combine queryOrderBy: and queryStartingAtValue::

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[ref queryOrderedByChild:@"height"] queryStartingAtValue:@3]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByChild("height").queryStartingAtValue(3)
   .observeEventType(.ChildAdded, withBlock: { snapshot in

    println(snapshot.key)
})

We can use queryEndingAtValue: to find all dinosaurs whose names come before Pterodactyl lexicographically:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[ref queryOrderedByKey] queryEndingAtValue:@"pterodactyl"]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByKey().queryEndingAtValue("pterodactyl")
   .observeEventType(.ChildAdded, withBlock: { snapshot in

    println(snapshot.key)
})
queryStartingAtValue: and queryEndingAtValue: are inclusive, meaning "pterodactyl" will match the query above.

We can combine queryStartingAtValue: and queryEndingAtValue: to limit both ends of our query. The following example finds all dinosaurs whose name starts with the letter "b":

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[[ref queryOrderedByKey] queryStartingAtValue:@"b"] queryEndingAtValue:@"b\uf8ff"]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByKey().queryStartingAtValue("b").queryEndingAtValue("b\u{f8ff}")
   .observeEventType(.ChildAdded, withBlock: { snapshot in

    println(snapshot.key)
})
The f8ff character used in the query above is a very high code point in the Unicode range. Because it is after most regular characters in Unicode, the query matches all values that start with a b.

The queryEqualToValue: method allows us to filter based on exact matches. As is the case with the other range queries, it will fire for each matching child node. For example, we can use the following query to find all dinosaurs which are 25 meters tall:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[ref queryOrderedByChild:@"height"] queryEqualToValue:@25]
    observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {

    NSLog(@"%@", snapshot.key);
}];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.queryOrderedByChild("height").queryEqualToValue(25)
   .observeEventType(.ChildAdded, withBlock: { snapshot in

    println(snapshot.key)
})

Range queries are also useful when you need to paginate your data.

Putting it all together

We can combine all of these techniques to create complex queries. For example, we can find the name of the dinosaur that is just shorter than Stegosaurus:

Firebase *ref = [[Firebase alloc] initWithUrl:@"https://dinosaur-facts.firebaseio.com/dinosaurs"];
[[[ref childByAppendingPath:@"stegosaurus"] childByAppendingPath:@"height"]
 observeEventType:FEventTypeValue withBlock:^(FDataSnapshot *stegosaurusHeightSnapshot) {
     NSNumber *favoriteDinoHeight = stegosaurusHeightSnapshot.value;
     FQuery *queryRef = [[[ref queryOrderedByChild:@"height"] queryEndingAtValue:favoriteDinoHeight] queryLimitedToLast:2];
     [queryRef observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *querySnapshot) {
         if (querySnapshot.childrenCount == 2) {
             for (FDataSnapshot* child in querySnapshot.children) {
                 NSLog(@"The dinosaur just shorter than the stegasaurus is %@", child.key);
                 break;
             }
         } else {
             NSLog(@"The stegosaurus is the shortest dino");
         }
     }];
 }];
let ref = Firebase(url:"https://dinosaur-facts.firebaseio.com/dinosaurs")
ref.childByAppendingPath("stegosaurus").childByAppendingPath("height")
    .observeEventType(.Value, withBlock: { stegosaurusHeightSnapshot in
        if let favoriteDinoHeight = stegosaurusHeightSnapshot.value as? Double {
            let queryRef = ref.queryOrderedByChild("height").queryEndingAtValue(favoriteDinoHeight).queryLimitedToLast(2)
            queryRef.observeSingleEventOfType(.Value, withBlock: { querySnapshot in
                if querySnapshot.childrenCount == 2 {
                    let child: FDataSnapshot = querySnapshot.children.nextObject() as FDataSnapshot
                    println("The dinosaur just shorter than the stegasaurus is \(child.key)");
                } else {
                    println("The stegosaurus is the shortest dino");
                }
            })
        }
    })

How Data is Ordered

This section explains how your data is ordered when using each of the three ordering functions.

queryOrderedByChild

When using queryOrderedByChild:, data that contains the specified child key will be ordered as follows:

  1. Children with a null value for the specified child key come first.
  2. Children with a value of false for the specified child key come next. If multiple children have a value of false, they are sorted lexicographically by key.
  3. Children with a value of true for the specified child key come next. If multiple children have a value of true, they are sorted lexicographically by key.
  4. Children with a numeric value come next, sorted in ascending order. If multiple children have the same numerical value for the specified child node, they are sorted by key.
  5. Strings come after numbers, and are sorted lexicographically in ascending order. If multiple children have the same value for the specified child node, they are ordered lexicographically by key.
  6. Objects come last, and sorted lexicographically by key name in ascending order.

queryOrderedByKey

When using queryOrderedByKey: to sort your data, data will be returned in ascending order by key name as follows. Keep in mind that keys can only be strings.

  1. Children with a key that can be parsed as a 32-bit integer come first, sorted in ascending order.
  2. Children with a string value as their key come next, sorted lexicographically in ascending order.

queryOrderedByValue

When using queryOrderedByValue, children will be ordered by their value. The ordering criteria is the same as in queryOrderedByChild:, except the value of the node is used instead of the value of a specified child key.

queryOrderedByPriority

When using queryOrderedByPriority: to sort your data, the ordering of children is determined by their priority and key as follows. Keep in mind that priority values can only be numbers or strings.

  1. Children with no priority (the default) come first.
  2. Children with a number as their priority come next. They are sorted numerically by priority, small to large.
  3. Children with a string as their priority come last. They are sorted lexicographically by priority.
  4. Whenever two children have the same priority (including no priority), they are sorted by key. Numeric keys come first (sorted numerically), followed by the remaining keys (sorted lexicographically).

For more information on priorities, see the API reference.

Now that we've covered retrieving data from our database, we're ready for the next article on structuring data.

  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