ZachtheBoB ZachtheBoB - 3 months ago 14
Swift Question

GCD Not Executing In Block Order

I am trying to check from my Firebase Database if certain nodes exist, and if not, create new ones in the Database. I need my method

loadAll()
to fully execute before it calls a second method that creates any missing nodes
autoCheck
. I tried a dispatch group to do this, but it doesn't work,
print("Done downloading!")
is called before it is done checking the database. Thanks!!

Code:

func loadAll(){
var deleted_load = false
var poor_load = false
var allLoadDone = false

if let user = FIRAuth.auth()?.currentUser {
let uid = user.uid

let refff = FIRDatabase.database().reference()
let userRef = refff.childByAppendingPath("users/\(uid)")


//When making new fields increase this var
var howmany = 2
var done = 0

var downloadGroup = dispatch_group_create()
dispatch_group_enter(downloadGroup)
userRef.queryOrderedByValue().observeEventType(.ChildAdded, withBlock: { snapshot in
allLoadDone = true
if(!snapshot.exists()){
print("ERR DOES NOT EXCIST")
self.autoCheck(deleted_load, poor_load: poor_load, userRef: userRef, ig: 1)
return
}
if let score = snapshot.value as? Int {
if(snapshot.key=="deleted"){
deleted_load = true
}
if(snapshot.key=="Staff_Poor"){
poor_load = true
}
print("\(snapshot.key) is \(score)")
self.counter.text = String(score)
}
done = done + 1
if(done>=(howmany)){
self.autoCheck(deleted_load, poor_load: poor_load, userRef: userRef, ig: 2)
}
})
dispatch_group_leave(downloadGroup)

dispatch_group_notify(downloadGroup, dispatch_get_main_queue()) { // 2
print("Done downloading!")
}
} else {
print("No user!")
gotoLogin()
}
}

func autoCheck(deleted_load: Bool, poor_load: Bool, userRef: FIRDatabaseReference, ig: Int) -> Bool{
print("ID IS: \(ig)")
var newUserData = ["deleted": 0, "Staff_Poor": 0]
print("deleted_load: \(deleted_load)")
if deleted_load==true{
newUserData.removeValueForKey("deleted")
}
print("poor_load: \(poor_load)")
if poor_load==true{
newUserData.removeValueForKey("Staff_Poor")
}
if(!newUserData.isEmpty){
userRef.updateChildValues(newUserData)
}

return true
}

Rob Rob
Answer

Your call to dispatch_group_leave(downloadGroup) must be placed inside the completion handler closure. Right now, you have it outside, which means that the group will be finished before the asynchronous calls are done.

So, replace

dispatch_group_enter(downloadGroup)
userRef.queryOrderedByValue().observeEventType(.ChildAdded, withBlock: { snapshot in
    ...
})
dispatch_group_leave(downloadGroup)

With

dispatch_group_enter(downloadGroup)
userRef.queryOrderedByValue().observeEventType(.ChildAdded, withBlock: { snapshot in
    ...
    dispatch_group_leave(downloadGroup)
})

Frankly, the use of dispatch group might not be appropriate here. The concept with dispatch groups that every "enter" is matched by a corresponding "leave". You're calling "enter" once, but it's not clear that you have any assurances how many times the observer will ultimately get called.

If once and only once, then this pattern is fine (though it would seem simpler to just put the dispatch notify code to be triggered inside the completion handler, itself, and retire the dispatch group entirely). If it might not get called or might get called multiple times, then having only one "enter" and with either zero or multiple "leaves" will break this pattern.