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
:
// 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?
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 functionupdateContacts
may not compute a value, but it may fail or succeed anyway. Then, you can use an optional error: if it isnil
, 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!
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: