Reputation: 4519
I have a method where I want to return results when it is ready. But i'm struggling with 2 completion handlers.
This is the code. The first completion handler is members.observesingleEvent. This returns a number of keys from a database. Then I loop over the keys to obtain new values from another call. I loop over the values and do the other call.
private func getRoomDevices(completionHandler : @escaping (Bool) -> ()){
members.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
let postDict = snapshot.value as! [String : AnyObject]
for list in postDict {
self.ref.child("groups/" + list.key).observeSingleEvent(of: .value, with: { groups in
self.rooms.append(FirebaseApi().convertRoomDevices(snap: groups))
print(groups.debugDescription)
});
}
completionHandler(true)
})
I add everything to the room array. But what happens is that completionHandler(true) is called while the for loop is busy. The completionHandler needs to be called when everything is done.
Upvotes: 1
Views: 173
Reputation: 54785
You have to use a DispatchGroup
, to which you add all items in your asynchronous loop and only call the completion handler if all items of the group finished execution.
members.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() { return }
let postDict = snapshot.value as! [String : AnyObject]
let group = DispatchGroup()
for list in postDict {
group.enter()
self.ref.child("groups/" + list.key).observeSingleEvent(of: .value, with: { groups in
self.rooms.append(FirebaseApi().convertRoomDevices(snap: groups))
print(groups.debugDescription)
group.leave()
});
}
group.notify(queue: DispatchQueue.main) {
completion(true)
}
})
Upvotes: 3