'Listen for documents just when actually added to collection - swift

I have a collection on Firestore and I listen for changes like this:

    func createMatchesListener(){

    let db = Firestore.firestore()
    guard let currentUid = Auth.auth().currentUser?.uid else { return }
    
    matchesListener = db.collection("Matches").document(currentUid).collection("Matches").addSnapshotListener({ snapshot, error in
        if let error = error{
            print(error.localizedDescription)
            return
        }
        
        snapshot?.documentChanges.forEach({ change in
            if change.type == .added{
                
               // do things
            }
        })
    })
}

I only want to listen for documents that are actually added to that collection. In fact, the problem is that whenever I invoke this function I receive all the documents of the collection as added documents and then I also receive documents added later.

How can I listen just for actually added later documents, ignoring the ones already present in the collection? Searching online I didn't find any solution to this issue.

EDIT: This is the way I tried to solve the problem:

func createMatchesListener(){
    guard let currentUid = Auth.auth().currentUser?.uid else { return }
    
    getUidsAlreadyMade { uidsAlreadyMade in
        matchesListener = db.collection("Matches").document(currentUid).collection("Matches").addSnapshotListener({ snapshot, error in
            if let error = error{
                print(error.localizedDescription)
                return
            }
            
            snapshot?.documentChanges.forEach({ change in
                if change.type == .added{
                    let data = change.document.data()
                    let userId = data["uid"] as? String ?? ""
                    
                    if uidsAlreadyMade.contains(userId) == false{
                        //means the uid is newly created in the collection, do stuff accordingly
                        
                        
                        arrayOfUidsAlreadyMade.append(currentUid)
                    }
                }
                if change.type == .removed{
                   // if the document has been removed, remove also the id from the array of uids

                    let data = change.document.data()
                    let currentUid = data["uid"] as? String ?? ""
                    arrayOfUidsAlreadyMade.removeAll { $0 == currentUid }
                }
            })
        })
    }
    
}

func getUidsAlreadyMade(completion: @escaping ([String]) -> Void){
    guard let currentUid = Auth.auth().currentUser?.uid else { return }
    db.collection("Matches").document(currentUid).collection("Matches").getDocuments { snapshot, error in
        if let error = error{
            print(error.localizedDescription)
            return
        }
        
        arrayOfUidsAlreadyMade.removeAll()
        
        snapshot?.documents.forEach({ doc in
            let dict = doc.data()
            let userId = dict["uid"] as? String ?? ""
            arrayOfUidsAlreadyMade.append(userId)
        })
        
        completion(arrayOfUidsAlreadyMade)
    }
}


Solution 1:[1]

You can store an array with the ID of the documents that you already have stored in the device. That way, all that you need to do before doing things is checking that document's id is not in your array

Solution 2:[2]

There's no way of preventing Firestore from returning the initial snapshot of documents when a document listener is added, so just use a boolean to keep track of the initial snapshot and ignore it.

var listenerDidInit = false

func createMatchesListener(){
    let db = Firestore.firestore()
    guard let currentUid = Auth.auth().currentUser?.uid else { return }
    
    matchesListener = db.collection("Matches").document(currentUid).collection("Matches").addSnapshotListener({ snapshot, error in
        if let error = error{
            print(error.localizedDescription)
            return
        }
        
        if listenerDidInit {
            snapshot?.documentChanges.forEach({ change in
                if change.type == .added{
                    // do things
                }
            })
        } else {
            listenerDidInit = true
        }
    })
}

Solution 3:[3]

A simple solution is to include a timestamp in your Firestore documents.

Suppose your documents store Tasks, for example

documentId
   task: "get dinner"
   timestamp: 20211123

and suppose your app doesn't care about past tasks, only new ones.

When the tasks are read, update the timestamp as to when that occurred.

Then each time after that you want to read only 'new data' specify that in your listener, keeping track of when the last read timestamp was:

db.collection("task").whereField("timestamp", isGreaterThan: lastReadTimeStamp).addSnapshotListener...

The above will only read in tasks that occured after the prior timestamp and add a Listener (reading in all of the new tasks so you can populate the UI).

Solution 4:[4]

private var listener: ListenerRegistration?


self.listener = db.collection("Matches") // matchesListener


listener!.remove()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pastre
Solution 2 fake girlfriends
Solution 3 Jay
Solution 4 Henry Ecker