swiftfirebasegoogle-cloud-firestore

Understanding Firestore addSnapshotListener: Does It Return All Matching Documents or Only New Inserts?


If a document with the 'success' status is inserted, does the addSnapshotListener callback return all documents with 'success' status (both new and existing) or only the newly inserted document with 'success' status?

private func monitorNoteChanges() {
    guard let uid = Auth.auth().currentUser?.uid else { return }

    self.stopMonitoring()
    
    let db = FirestoreUtils.firestore()
    
    // Reference to the notes subcollection for the specific user
    let notesRef = db.collection("users").document(uid).collection("notes")
    
    let statusValues = [
        Status.success.rawValue
    ]
    
    // Query to listen for changes to documents where status matches the specified value
    let query = notesRef.whereField("status", in: statusValues).whereField("trashed", isEqualTo: false)
    
    // Attach a listener and store the ListenerRegistration
    let listener = query.addSnapshotListener { [weak self] (querySnapshot, error) in
        guard let self = self else { return }
        
        guard let documents = querySnapshot?.documents else {
            print("No documents or an error occurred: \(error?.localizedDescription ?? "Unknown error")")
            return
        }
        
        // TODO: documents
    }
    
    self.listener = listener
}

Initially, I thought that only newly inserted documents would be returned by the listener.

However, I’ve noticed that whenever a new document is inserted, both the new document and all existing matching documents are returned.

Is this behavior correct and guaranteed?


Solution

  • Since you loop over querySnapshot?.documents, you will always get all matching documents.

    If you want to handle the specific change that was made, loop over querySnapshot?.documentChanges and check the type of each diff in there.

    For more on this, see the documentation on viewing changes between snapshots. The code sample from there:

    db.collection("cities").whereField("state", isEqualTo: "CA")
      .addSnapshotListener { querySnapshot, error in
        guard let snapshot = querySnapshot else {
          print("Error fetching snapshots: \(error!)")
          return
        }
        snapshot.documentChanges.forEach { diff in
          if (diff.type == .added) {
            print("New city: \(diff.document.data())")
          }
          if (diff.type == .modified) {
            print("Modified city: \(diff.document.data())")
          }
          if (diff.type == .removed) {
            print("Removed city: \(diff.document.data())")
          }
        }
      }
    

    So if you added one new document, the snapshot.documentChanges will have only one entry with diff.type == .added .