Ensuring Async Action Has Completed in Firebase

I need to run concurrent queries to Firebase in swift. How can I ensure that a looped query has finished before another action is allowed to start in my app?

For example, the first query is a straightforward, and simply pulls data. but the second, iterates through an array looking for a certain node in my firebase database, looping through the array self.contacts:

  • iOS GCD Sync with Async Block
  • Firebase & swift use .indexOn to sort data snapshot
  • Firebase with Swift
  • Firestore: How to get random documents in a collection
  • Firebase Cloud Messaging - Check existing or available Topics
  • How to add Firebase to Today Extension iOS
  •  // First check if friend, and remove/add to
                friendsURL.observeSingleEventOfType(.Value, withBlock: { snapshot in
    
                    for oneSnapshot in snapshot.children {
    
                        for oneContact in contactsArray {
    
                            for oneContactPhoneNum in oneContact.phoneNumbers {
    
                                let phoneNumber = oneContactPhoneNum.value as! CNPhoneNumber
    
                                contactNumber = phoneNumber.stringValue
    
                                // Clean the number
                                let stringArray = contactNumber!.componentsSeparatedByCharactersInSet(
                                    NSCharacterSet.decimalDigitCharacterSet().invertedSet)
                                let newString = "1" + stringArray.joinWithSeparator("")
    
                                let firebaseFriendNumber = oneSnapshot.value["phoneNumber"] as! String
    
                                if newString == firebaseFriendNumber {
    
                                    self.friends.append(Friend(userName: oneSnapshot.value["userName"] as! String,phoneNumber: firebaseFriendNumber, status: 2, name: oneContact.givenName, userID: oneSnapshot.key))
    
                                    // Remove that contact
                                    self.contacts.removeObject(oneContact)
                                }
                            }
                        }
                    }
    
                    // Now do the users search:
                    for oneContact in self.contacts {
                        for oneContactNumer in oneContact.phoneNumbers {
    
                            let phoneNumber = oneContactNumer.value as! CNPhoneNumber
    
                            contactNumber = phoneNumber.stringValue
    
                            let stringArray = contactNumber!.componentsSeparatedByCharactersInSet(
                                NSCharacterSet.decimalDigitCharacterSet().invertedSet)
                            let newString = "1" + stringArray.joinWithSeparator("")
    
                            let usersURL: Firebase! = Firebase(url: firebaseMainURL + "presentUserIDUserNameByPhoneNumber/" + newString)
    
                            // Check db:
    
                            usersURL.observeSingleEventOfType(.Value, withBlock: { snapshot in
    
                                if snapshot.childrenCount > 1 {
    
                                    // They are users (but not your friends):
                                    self.friends.append(Friend(userName: snapshot.value["userName"] as! String, phoneNumber: snapshot.key, status: 1, name: "test", userID: snapshot.value["userID"] as! String))
    
                                    let userName = snapshot.value["userName"] as! String
    
                                    print("Friends name: " + userName)
    
                                    // Remove that contact
                                    self.contacts.removeObject(oneContact)
    
                                }
    
                            })
    
                        }
    
                    }
    
                })
    

    How can I test and check when the second, on usersURL, has completed before allowing other actions to occur in app?

    Solutions Collect From Internet About “Ensuring Async Action Has Completed in Firebase”

    One approach to signal completion of an asynchronous function is using a completion handler. You already used completion handlers in the Firebase API and there are many APIs in the system frameworks, so I don’t explain that further.

    Given this approach, wrap your code into a function, say updateContacts with a completion handler. Usually an asynchronous function returns the computed value or an error. In some cases, it just succeeds or fails – without returning a value. You express this in the signature of the completion handler. Your function updateContacts may not compute a value, but it may fail or succeed anyway. Then, you can use an optional error: if it is nil, the task succeeded, otherwise it contains the error that occurred.

    When your underlying task has been completed, call the completion handler with the result.

    Note: You must ensure, that the completion handler will be eventually called!

    func updateContacts(completion: (ErrorType?)-> ()) {
        friendsURL.observeSingleEventOfType(.Value, withBlock: { snapshot in
            ...
                ...
                    ...
                        usersURL.observeSingleEventOfType(.Value, withBlock: { snapshot in
                            ...
                            let error = // nil or an error
                            completion(error)
                           return
                         }
            completion(nil)
        }    
    }
    

    Now, when you have an array of asynchronous subtasks, that will be called in parallel and you want to signal completion of updateContacts when all subtasks have been completed – you can utilise dispatch groups:

    let group = dispatch_group_create()
    var error: ErrorType?
    contactNumbers.forEach { number in
        dispatch_group_enter(group)
        queryAsync(number) { (result, error) in
            if let error = error {
                // handle error
            } else {
                ...
            }             
            dispatch_group_leave(group)
        }
    }
    dispatch_group_notify(group, queue) {
        // call the completion handler of your function `updateContacts`:
        completion(error)
    }