1

I am relatively new to async functions and I understand that the firebase getDocument and getDocuments calls are async. I would like both of these calls to finish before I move on to what I was doing in the code. I've been trying to implement this with dispatch groups, but have been unsuccessful thus far. I have code like the following:

                
                let myGroup = DispatchGroup()
                self.errorMessage = ""
                let usersRef = self.db.collection("Users").document("Users").collection("Users")
                if self.test == false {
                    self.errorMessage = "test failed"
                } else{
                    //first async call
                    myGroup.enter()
                    usersRef.getDocuments {(snap, err) in
                        //basically getting every username
                        for document in snap!.documents{
                            print("loop")
                            let user = document["username"] as! String
                            let userRef = usersRef.document(user)
                            //second async call
                            userRef.getDocument { (snapshot, err) in
                                if err != nil {
                                    print(err)
                                } else {
                                    let self.error = snapshot!["error"] as! Bool
                                    if self.error == true{
                                        self.errorMessage = "error"
                                        print("error")
                                    }
                                    print("what3")
                                }
                                print("what2")
                            }
                            print("what1")
                        }
                        myGroup.leave()
                        print("what4")
                    }
                    //RIGHT HERE I WANT TO CONTINUE WHAT I WAS DOING BEFORE
                    myGroup.notify(queue: DispatchQueue.global(qos: .background)) {
                        print("HERE I SHOULD BE DONE")
                    }
                    
                    print("what5")
                }

However, this produces something like:

what5
loop
what1
loop
what1
loop
what1
loop
what1
loop
what1
loop
what1
what4
HERE I SHOULD BE DONE
error
what3
what2
error
what3
what2
what3
what2
error
what3
what2
what3
what2
error
what3
what2

So it seems like the FIRST async call is finishing, but then the second continues executing. I'd like to wait for the second to finish before continuing.

Any advice on how to modify this code would be greatly appreciated. Thanks.

2 Answers 2

2

You need to re-enter a the group again when doing the second getDocuments call. As it will also be asynchron. Something like this should do the trick:

let myGroup = DispatchGroup()
        //Enter first time for first async call
         myGroup.enter()
         self.errorMessage = ""
         let usersRef = self.db.collection("Users").document("Users").collection("Users")
         if self.test == false {
             self.errorMessage = "test failed"
         } else{
            usersRef.getDocuments {(snap, err) in //Starting first async call
                
                for document in snap!.documents{
                    print("loop")
                    let user = document["username"] as! String
                    let userRef = usersRef.document(user)
                    
                    //Enter second time for second async call
                    myGroup.enter()
                    userRef.getDocument { (snapshot, err) in // starting second async call
                        if err != nil {
                            print(err)
                        } else {
                            let self.error = snapshot!["error"] as! Bool
                            if self.error == true{
                                self.errorMessage = "error"
                                print("error")
                            }
                            print("what3")
                        }
                        print("what2")
                        //Leave second async call
                        myGroup.leave()
                    }
                    print("what1")
                }
                //Leave first async call
                myGroup.leave()
                print("what4")
             }

             myGroup.notify(queue: DispatchQueue.global(qos: .background)) {
                 print("HERE I SHOULD BE DONE")
             }
             
             print("what5")
         }
        
    }

Recommendation: When using DispatchGroup/ Asynchron calls try to divide them. For e.g. use a function for each call as it can get really quickly really messy. Having them separated and then combining them in one method makes it also easier to modify and or find errors.

Sign up to request clarification or add additional context in comments.

2 Comments

wow thanks so much for this, it was exactly what I needed and I'll be referring to it for quite some time to come. I know what you mean and in the future I'll try and separate the async calls into functions. As a follow up question, I understand that you can't leave a dispatch group more times than you enter it, but from this example, it seems like you can enter a dispatch group more times than you leave it? and when the number of times entered == the number of times left, the notify() function gets called?
You do leave it as many times as you enter it. Here in this e.g. You enter it one time at the start. This the gets left at the ending when the getDocuments is done. Also you enter it each time before you getDocument in the for loop and leave it in the response of that too. You have to leave it as many times as you enter it otherwise it stays blocked.
1

Nested asynchronous code can be pain in head and it's much difficult to manage without having a strong grasp on Grand-Central-Dispatch.I 'll recommend you to use libraries like AwaitKit or PromiseKit which are specifically designed for that purpose. It 'll take some time to get a good grip on these libraries, but once done, they will be very useful in such situations and you 'll be able to deal with asynchronous code in synchronous manner. You can check these libraries here

1 Comment

okay cool thanks for the response - I'll look into them

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.