Home > Software engineering >  How to wait for multiple firebase queries to finish before executing a task?
How to wait for multiple firebase queries to finish before executing a task?

Time:09-08

I'm trying to figure out how to delete all saved User's data, and once that data is deleted, Signout the user and delete their account when deleteUserAccount() is called.

Right now, when the User presses the "Delete Account" button, their account gets deleted, but the data still remains on firebase because the completion handler gets called before the deleteAllUserTicketData() function has enough time to execute the query and delete the appropriate documents.

How can I wait for the queries to execute and all the documents to get deleted before executing the completion handler in order to sign the user out and delete their account?

func deleteAllUserTicketData(completion: @escaping () -> Void) {
    self.rootWsrCollection?.whereField("uid", isEqualTo: userIdRef).addSnapshotListener { (querySnapshot, err) in
        guard let snapshot = querySnapshot else { return }
        for wsr in snapshot.documents{
            print("Deleting WSR's")
            self.rootWsrCollection!.document(wsr.documentID).delete()
        }
    }

    self.rootExerciseCollection?.whereField("uid", isEqualTo: userIdRef).addSnapshotListener { (querySnapshot, err) in
        guard let snapshot = querySnapshot else { return }
        for exercise in snapshot.documents {
            print("Deleting Exercises")
            self.rootExerciseCollection!.document(exercise.documentID).delete()
        }
    }

    self.rootWorkoutsCollection?.whereField("uid", isEqualTo: userIdRef).addSnapshotListener { (querySnapshot, err) in
        guard let snapshot = querySnapshot else { return }
        for workout in snapshot.documents {
            print("Deleting Workouts")
            self.rootWorkoutsCollection!.document(workout.documentID).delete()
        }
    }

    self.workoutsCollection.daysCollection.removeAll()
    completion()
}


func deleteUserFromFirestore(completion: @escaping () -> Void) {
    self.rootUserCollection?.whereField("uid", isEqualTo: userIdRef).addSnapshotListener { (querySnapshot, err) in
        guard let snapshot = querySnapshot else { return }
        for user in snapshot.documents {
            self.rootUserCollection!.document(user.documentID).delete()
        }
        completion()
    }
}


func deleteUserAccount() {
    deleteAllUserTicketData {
        self.deleteUserFromFirestore {
            Auth.auth().currentUser?.delete()
        }
    }
}

CodePudding user response:

You can try DispatchGroup to wait for all async then trigger when they all finish. If you enter() 5 times, you also need to leave() 5 times. You need to balance the leave() with enter(). If you call leave() more than enter(), it will crash because unbalanced call.

And, please do not use addSnapShotListener if you don't need to make a realtime observer. Use this instead https://firebase.google.com/docs/firestore/query-data/get-data#get_a_document

Here is you updated logic:

        func deleteAllUserTicketData(completion: @escaping () -> Void) {
            let dispatchGroup = DispatchGroup()

            dispatchGroup.enter()
            self.rootWsrCollection?.whereField("uid", isEqualTo: userIdRef).addSnapshotListener {[weak self] (querySnapshot, err) in
                guard let snapshot = querySnapshot else { return }
                for wsr in snapshot.documents{
                    print("Deleting WSR's")
                    self?.rootWsrCollection!.document(wsr.documentID).delete()
                    // use can try this for shortened call
                    wsr.delete()
                }
                // Suppose that delete() is success
                dispatchGroup.leave()
            }

            dispatchGroup.enter()
            self.rootExerciseCollection?.whereField("uid", isEqualTo: userIdRef).addSnapshotListener {(querySnapshot, err) in
                guard let snapshot = querySnapshot else { return }
                for exercise in snapshot.documents {
                    print("Deleting Exercises")
                    exercise.delete()
                }
                dispatchGroup.leave()
            }

            dispatchGroup.enter()
            self.rootWorkoutsCollection?.whereField("uid", isEqualTo: userIdRef).addSnapshotListener { (querySnapshot, err) in
                guard let snapshot = querySnapshot else { return }
                for workout in snapshot.documents {
                    print("Deleting Workouts")
                    workout.delete()
                }
                dispatchGroup.leave()
            }
            
            // When no waiting item remains (all entered and left), this block will be triggered on queue, in this case, is it Global queue (like background thread
            // So if you need to update UI from this, you need to switch to Main Queue or DispatchQueue.main.async { }
            dispatchGroup.notify(queue: DispatchQueue.global()) { [weak self] in
                // refer to self with a weak reference to avoid retain cycle, prevent memory leak
                self?.workoutsCollection.daysCollection.removeAll()
                completion()
            }
        }

        func deleteUserFromFirestore(completion: @escaping () -> Void) {
            self.rootUserCollection?.whereField("uid", isEqualTo: userIdRef).addSnapshotListener { (querySnapshot, err) in
                guard let snapshot = querySnapshot else { return }
                for user in snapshot.documents {
                    user.delete()
                }
                completion()
            }
        }
    
        func deleteUserAccount() {
            self.deleteAllUserTicketData { [weak self] in
                self?.deleteUserFromFirestore {
                    Auth.auth().currentUser?.delete()
                }
            }
        }
  • Related