Home > Net >  Swift for loop not waiting for firestore call to complete
Swift for loop not waiting for firestore call to complete

Time:05-15

I know firestore calls are async which means this will not work:

    private func removeUserSavedMomentFromAllUsers(moment: StoryMoment, completion: @escaping () -> Void) {
        guard let savedByUIDs = moment.savedByUIDs else { return }
        guard let momentID = moment.id else { return }
        for id in savedByUIDs {
            self.userInfoCollection.document(id).collection("savedMedias").document(momentID).delete { error in
                if let error = error {
                    print("Error removing user saved moment from UID: \(error)")
                }
            }
        }
    }

Since the loop will continue before the delete call completes (same with get requests). I have used dispatch groups in the past to solve this issue. Heres a working example:

    private func removeUserSavedMomentFromAllUsers(moment: StoryMoment, completion: @escaping () -> Void) {
        guard let savedByUIDs = moment.savedByUIDs else { return }
        guard let momentID = moment.id else { return }
        let disSemaphore = DispatchSemaphore(value: 0)
        let dispatchQueue = DispatchQueue(label: "group 1")
        
        dispatchQueue.async {
            for id in savedByUIDs {
                self.userInfoCollection.document(id).collection("savedMedias").document(momentID).delete { error in
                    if let error = error {
                        print("Error removing user saved moment from UID: \(error)")
                    } else {
                        disSemaphore.signal()
                    }
                }
                disSemaphore.wait()
            }
        }
    }

But those do all the work on the background thread. My question is: How can I use async/await in a for loop where you call firebase docs?

CodePudding user response:

The code in the first part of the question does work - and works fine for small group of data. However, in general it's recommended to not call Firebase functions in tight loops.

While the question mentions DispatchQueues, we use DispatchGroups with .enter and .leave as it's pretty clean.

Given a Firebase structure

sample_data
   a
      key: "value"
   b
      key: "value"
   c
      key: "value"
   d
      key: "value"
   e
      key: "value"
   f
      key: "value"

and suppose we want to delete the d, e, and f documents. Here's the code

func dispatchGroupDelete() {
    let documentsToDelete = ["d", "e", "f"]
    let collection = self.db.collection("sample_data") //self.db points to my Firestore
    let group = DispatchGroup()
    
    for docId in documentsToDelete {
        group.enter()
        let docToDelete = collection.document(docId)
        docToDelete.delete()
        group.leave()
    }
}

While this answer doesn't use async/await, those may not be needed for this use case

If you want to use async/await you try do this

let documentsToDelete = ["d", "e", "f"]
let collection = self.db.collection("sample_data")

for docId in documentsToDelete {
    let docToDelete = collection.document(docId)
    Task {
        do {
            try await docToDelete.delete()
        } catch {
            print("oops")
        }
    }
}
  • Related