Q: How do I perform a pagination query on Firebase's iOS SDK? 
This is my model. 
messagesRef = Firebase(url: "https://"+CONSTANTS.FirebaseDB+".firebaseio.com/messages/1:1000")
    messagesRef.queryLimitedToLast(5).observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) in
        self.message_list.append(snapshot) // it works.
    });
});

My code works -- it gets the last 5 messages (8-12).
However, what if I have a function query the next 5 messages (2-6)? With a start and offset. How can I query this? 

A: messagesRef = Firebase(url: "https://"+CONSTANTS.FirebaseDB+".firebaseio.com/messages/1:1000")messagesRef
.queryOrderedByKey()
.queryStartingAtValue(5)
.queryEndingAtValue(10)
.observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) in self.message_list.append(snapshot) });

This is kind of a shot in the dark but it seems like it should work based on documentation here https://www.firebase.com/docs/ios-api/Classes/Firebase.html#//api/name/queryStartingAtValue:

A: On spending too much time I have figured it out and here is the solution. This is Objective-C code you can convert it into swift.
Call below function for paging purpose.
- (void)loadMoreMessages {
    
    if (!lastMessageKey) {
        // Loading messages first time
        [[[msgsReference queryOrderedByKey] queryLimitedToLast:K_MESSAGES_PER_PAGE] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
            if (snapshot.exists) {
                
                for (FIRDataSnapshot *child in snapshot.children) {
                   
                    NSMutableDictionary *dict = [child.value mutableCopy];
                    [dict setObject:child.key forKey:@"id"];
                    [messages addObject:dict];
                }
                
                lastMessageKey = [[snapshot.children.allObjects firstObject] key];
                NSLog(@"%@", messages);
            }
        }];
    }
    else {
        // Paging started
        [[[[msgsReference queryOrderedByKey] queryLimitedToLast:K_MESSAGES_PER_PAGE + 1] queryEndingAtValue:lastMessageKey] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
           
            if (snapshot.exists) {
       
                NSInteger count = 0;
                NSMutableArray *newPage = [NSMutableArray new];
                for (FIRDataSnapshot *child in snapshot.children) {
                    
                    // Ignore last object because this is duplicate of last page
                    if (count == snapshot.childrenCount - 1) {
                        break;
                    }
                    
                    count += 1;
                    NSMutableDictionary *dict = [child.value mutableCopy];
                    [dict setObject:child.key forKey:@"id"];
                    [newPage addObject:dict];
                }
                
                lastMessageKey = [[snapshot.children.allObjects firstObject] key];

                // Insert new messages at top of old array
                NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [newPage count])];
                [messages insertObjects:newPage atIndexes:indexes];
                NSLog(@"%@", messages);
            }
        }];
    }
}

And here is description of objects you needed:
#define K_MESSAGES_PER_PAGE 50 // A macro defining the numbers in one request
msgsReference // Firebase database messages node reference I'm also attaching the screenshot of my db structure for make you more clear
lastMessageKey // Is a NSString object which store the first key of last page
messages // Is a NSMutableArray storing the result

Good Luck!! (y)


A: Swift 3.x
func fetchEarlierMessages(chatGroupId: String, messageKey: String, completion: @escaping ([Message]?) -> ()) {
    previousMessageRef = root.child("messages").child(chatGroupId)
    messageValueHandle = previousMessageRef.queryOrderedByKey().queryLimited(toLast: 10).queryEnding(atValue: messageKey).observe(.value, with: { snapshot in
        var messages: [Message] = []
        for child in snapshot.children.allObjects as! [FIRDataSnapshot] {
            guard var item = child.value as? [String: AnyObject] else { return }
            item["message_id"] = child.key as AnyObject
            if let message = self.parseMessage(snapshot: child) {
                messages.append(message)
            }
        }
        messages.removeLast()
        completion(messages)
    })
}

Here the 'parseMessage' function is my custom function to convert snapshot into message model, you can use your own. the message key is the key of earliest message you loaded in initial firebase call. 

A: Swift 2017
I'd like to present an as elegant solution as i could made for this task of pagination with the firebase. This method called both when initializing and loading more data:
var items: [ItemEntity] = []
var theEndOfResults = false
var lastLoadedItem: ItemEntity? {
    return items.last
}
    
func loadItems() {
    let isLoadingMore = lastLoadedItem != nil
        
    Decorator.showStatusBarLoader()
    self.databaseReference
        .child("items")
        .queryOrdered(byChild: "date")
        .queryEnding(atValue: isLoadingMore ? lastLoadedItem!.date.stringValue : Date.distantFuture.stringValue)
        .queryLimited(toLast: 5)
        .observeSingleEvent(of: .value) { snapshot in
            var items = self.array(from: snapshot)
                .map { ItemEntity(parentKey: $0.parentKey, dictionary: $0.dict) }

                self.theEndOfResults = (self.lastLoadedItem == items.last) // prevent loading when it's no more items
                if isLoadingMore { items.removeFirst() } // removing as the firebase sending a repeated one
                self.items.append(contentsOf: items)

                self.reloadData()
        }
    }

The function for reloading data in controller.
override func reloadData() {
    tableV.reloadData()
    refreshControl.endRefreshing()
    tableV.loadControl?.endLoading()
    Decorator.hideStatusBarLoader()
}

This is called when a user reach the end of tableView.
@objc public func loadMore() {
    guard self.theEndOfResults == false else { tableV.loadControl?.endLoading(); return }
    self..loadItems()
}

Making array from snapshot
func array(from snapshot: DataSnapshot) -> [ParseResult] {
    var items = [ParseResult]()
    if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
        for snap in snapshots {
            if let postDictionary = snap.value as? Dictionary<String, AnyObject> {
                items.append((snap.key, postDictionary))
            }
        }
    }
    print(" DATA COME:\r\(snapshot)")
    //or u can use: dump(items, indent: 2, maxDepth: 5, maxItems: 15)
    return items.reversed()
}

Thanks this video for clarifying some moments with "cool" firebase reference api.
Good luck in development and ask any questions if found something unclear.
