Reputation: 2040
I understand that the Firebase getDocument call is Async, so I'm trying to figure out how to essentially wait until the call finishes executing, and then move on to doing other stuff.
I have tried making use of DispatchGroup() and entering/leaving the group, but I can't seem to get it to work correctly. I have something like the following:
let myGroup = DispatchGroup()
let usersRef = self.db.collection("Users").document("Users").collection("Users")
if self.testCondition == false {
self.errorMessage = "error"
} else{
usersRef.getDocuments {(snap, err) in
myGroup.enter()
//basically getting every username
for document in snap!.documents{
let user = document["username"] as! String
let userRef = usersRef.document(user)
userRef.getDocument { (snapshot, err) in
if err != nil {
print(err)
} else {
let sample = snapshot!["sample"] as! String
if sample == 'bad' {
self.errorMessage = "error"
}
}
}
}
myGroup.leave()
}
print("what4")
//I would like it so that I can execute everything in a code block like this
//after the async call finishes
myGroup.notify(queue: .main) {
print("Finished all requests.")
//THEN DO MORE STUFF
}
}
How can I modify the placement myGroup.enter() and myGroup.leave() in this so that, after the Firebase call has finished, I can continue executing code?
Thanks!
Upvotes: 2
Views: 1347
Reputation: 1850
This explains the DispatchGroup()
a little bit.
You just have one litte mistake in your code then it should be working.
Make sure to enter()
the group outside of the Firebase getDocuments()
call. As this already makes the request and takes time thus the process will continue.
This little simple example should help you understand it:
func dispatchGroupExample() {
// Initialize the DispatchGroup
let group = DispatchGroup()
print("starting")
// Enter the group outside of the getDocuments call
group.enter()
let db = Firestore.firestore()
let docRef = db.collection("test")
docRef.getDocuments { (snapshots, error) in
if let documents = snapshots?.documents {
for doc in documents {
print(doc["name"])
}
}
// leave the group when done
group.leave()
}
// Continue in here when done above
group.notify(queue: DispatchQueue.global(qos: .background)) {
print("all names returned, we can continue")
}
}
When waiting for multiple asynchronous calls use completing
in the asynchronous function which you let return as soon as you leave the group. Full eg. below:
class Test {
init() {
self.twoNestedAsync()
}
func twoNestedAsync() {
let group = DispatchGroup() // Init DispatchGroup
// First Enter
group.enter()
print("calling first asynch")
self.dispatchGroupExample() { isSucceeded in
// Only leave when dispatchGroup returns the escaping bool
if isSucceeded {
group.leave()
} else {
// returned false
group.leave()
}
}
// Enter second
group.enter()
print("calling second asynch")
self.waitAndReturn(){ isSucceeded in
// Only return once the escaping bool comes back
if isSucceeded {
group.leave()
} else {
//returned false
group.leave()
}
}
group.notify(queue: .main) {
print("all asynch done")
}
}
// Now added escaping bool which gets returned when done
func dispatchGroupExample(completing: @escaping (Bool) -> Void) {
// Initialize the DispatchGroup
let group = DispatchGroup()
print("starting")
// Enter the group outside of the getDocuments call
group.enter()
let db = Firestore.firestore()
let docRef = db.collection("test")
docRef.getDocuments { (snapshots, error) in
if let documents = snapshots?.documents {
for doc in documents {
print(doc["name"])
}
// leave the group when succesful and done
group.leave()
}
if let error = error {
// make sure to handle this
completing(false)
group.leave()
}
}
// Continue in here when done above
group.notify(queue: DispatchQueue.global(qos: .background)) {
print("all names returned, we can continue")
//send escaping bool.
completing(true)
}
}
func waitAndReturn(completing: @escaping (Bool) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2), execute: {
print("Done waiting for 2 seconds")
completing(true)
})
}
}
This gives us the following output:
Upvotes: 5