Firestore Paginating data + Snapshot listener

I am working with Firestore right now and have a little pagination problem.
Basically, I have a collection (suppose 10 items) where each item has some data and a timestamp.

Now I load the first 3 elements like this:

Firestore.firestore()
    .collection("collectionPath")
    .order(by: "timestamp", descending: true)
    .limit(to: 3)
    .addSnapshotListener(snapshotListener())

Inside my snapshot receiver, I save the last document from the snapshot to use as a starting point for my next page.

So, at some point I will ask for the following page of such elements:

Firestore.firestore()
    .collection("collectionPath")
    .order(by: "timestamp", descending: true)
    .start(afterDocument: lastDocument)
    .limit(to: 3)
    .addSnapshotListener(snapshotListener2()) // Note that this is a new snapshot listener, I don't know how I could reuse the first one

Now I have elements from index 0 to index 5 (total 6) in my interface. Well maintained!

4 , .
, - !

, , , 6 ( )

, 5 , , , .

- Firestore?

EDIT: :
, . , , , , ,

private func snapshotListener() -> FIRQuerySnapshotBlock {
    let index = self.index
    return { querySnapshot, error in
        guard let snap = querySnapshot, error == nil else {
            log.error(error)
            return
        }

        // Save the last doc, so we can later use pagination to retrieve further chats
        if snap.count == self.limit {
            self.lastDoc = snap.documents.last
        } else {
            self.lastDoc = nil
        }

        let offset = index * self.limit

        snap.documentChanges.forEach() { diff in
            switch diff.type {
            case .added:
                log.debug("added chat at index: \(diff.newIndex), offset: \(offset)")
                self.tVHandler.dataManager.insert(item: Chat(dictionary: diff.document.data() as NSDictionary), at: IndexPath(row: Int(diff.newIndex) + offset, section: 0), in: nil)

            case .removed:
                log.debug("deleted chat at index: \(diff.oldIndex), offset: \(offset)")
                self.tVHandler.dataManager.remove(itemAt: IndexPath(row: Int(diff.oldIndex) + offset, section: 0), in: nil)

            case .modified:
                if diff.oldIndex == diff.newIndex {
                    log.debug("updated chat at index: \(diff.oldIndex), offset: \(offset)")
                    self.tVHandler.dataManager.update(item: Chat(dictionary: diff.document.data() as NSDictionary), at: IndexPath(row: Int(diff.oldIndex) + offset, section: 0), in: nil)
                } else {
                    log.debug("moved chat at index: \(diff.oldIndex), offset: \(offset) to index: \(diff.newIndex), offset: \(offset)")
                    self.tVHandler.dataManager.move(item: Chat(dictionary: diff.document.data() as NSDictionary), from: IndexPath(row: Int(diff.oldIndex) + offset, section: 0), to: IndexPath(row: Int(diff.newIndex) + offset, section: 0), in: nil)
                }
            }
        }
        self.tableView?.reloadData()
    }
}

, , , , Firestore

+8
3

, Firebase Google Group, , .
!

, , . thread

+3

Objective C. , - , , google-cloud-firestore .

: , , , , .

: , , , , 1:

1. , , :

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
     [self fetchChats];
}

-(void)fetchChats {
    __weak typeof(self) weakSelf = self;
     FIRQuery *paginateChatsQuery = [[[self.db collectionWithPath:MAGConstCollectionNameChats]queryOrderedByField:MAGConstFieldNameTimestamp descending:YES]queryLimitedTo:MAGConstPageLimit];
    if(self.arrChats.count > 0){
        FIRDocumentSnapshot *lastChatDocument = self.arrChats.lastObject;
        paginateChatsQuery = [paginateChatsQuery queryStartingAfterDocument:lastChatDocument];
    }
    [paginateChatsQuery getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
        if (snapshot == nil) {
            NSLog(@"Error fetching documents: %@", error);
            return;
        }
        ///2. Observe chat updates if not attached
        if(weakSelf.chatObserverState == ChatObserverStateNotAttached) {
            weakSelf.chatObserverState = ChatObserverStateAttaching;
            [weakSelf observeChats];
        }

        if(snapshot.documents.count < MAGConstPageLimit) {
            weakSelf.noMoreData = YES;
        }
        else {
            weakSelf.noMoreData = NO;
        }

        [weakSelf.arrChats addObjectsFromArray:snapshot.documents];
        [weakSelf.tblVuChatsList reloadData];
    }];
}

2. "fetchAlerts" , 1.

-(void)observeChats {
    __weak typeof(self) weakSelf = self;
    self.chatsListener = [[[[self.db collectionWithPath:MAGConstCollectionNameChats]queryOrderedByField:MAGConstFieldNameTimestamp descending:YES]queryLimitedTo:1]addSnapshotListener:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
        if (snapshot == nil) {
            NSLog(@"Error fetching documents: %@", error);
            return;
        }
        if(weakSelf.chatObserverState == ChatObserverStateAttaching) {
            weakSelf.chatObserverState = ChatObserverStateAttached;
        }

        for (FIRDocumentChange *diff in snapshot.documentChanges) {
            if (diff.type == FIRDocumentChangeTypeAdded) {
                ///New chat added
                NSLog(@"Added chat: %@", diff.document.data);
                FIRDocumentSnapshot *chatDoc = diff.document;
                [weakSelf handleChatUpdates:chatDoc];

            }
            else if (diff.type == FIRDocumentChangeTypeModified) {
                NSLog(@"Modified chat: %@", diff.document.data);
                FIRDocumentSnapshot *chatDoc = diff.document;
                [weakSelf handleChatUpdates:chatDoc];
            }
            else if (diff.type == FIRDocumentChangeTypeRemoved) {
                NSLog(@"Removed chat: %@", diff.document.data);
            }
        }
    }];

}

3. FIRDocumentChangeTypeAdded FIRDocumentChangeTypeModified FIRDocumentChangeTypeRemoved. , handleChatUpdates FIRDocumentChangeTypeAdded FIRDocumentChangeTypeModified, , , , , .

-(void)handleChatUpdates:(FIRDocumentSnapshot *)chatDoc {
    NSInteger chatIndex = [self getIndexOfMatchingChatDoc:chatDoc];
    if(chatIndex != NSNotFound) {
        ///Remove this object
        [self.arrChats removeObjectAtIndex:chatIndex];
    }
    ///Insert this chat object at the beginning of the array
     [self.arrChats insertObject:chatDoc atIndex:0];

    ///Refresh the tableview
    [self.tblVuChatsList reloadData];
}

-(NSInteger)getIndexOfMatchingChatDoc:(FIRDocumentSnapshot *)chatDoc {
    NSInteger chatIndex = 0;
    for (FIRDocumentSnapshot *chatDocument in self.arrChats) {
        if([chatDocument.documentID isEqualToString:chatDoc.documentID]) {
            return chatIndex;
        }
        chatIndex++;
    }
    return NSNotFound;
}

4. , .

0

My solution is to create 1 follower request - a listener who will watch the deleted item from the first request, and we will update it every time a new message arrives.

0
source

Source: https://habr.com/ru/post/1688949/


All Articles