5

I have two collections namely, Users and Questions.

Based on the user logged in using userId, I retrieve the currQuestion value from users collection.

Based on the currQuestion value, I need to retrieve the question document from Firebase Questions collection.

I've used the below code to retrieve userId

rootRef.child("0").child("users")
        .queryOrderedByChild("userId")
        .queryEqualToValue("578ab1a0e9c2389b23a0e870")
        .observeSingleEventOfType(.Value, withBlock: { (snapshot) in

            for child in snapshot.children {
                self.currQuestion = child.value["currentQuestion"] as! Int
            }
            print("Current Question is \(self.currQuestion)")

            //print(snapshot.value as! Array<AnyObject>)
        }, withCancelBlock : { error in
                print(error.description)
        })

and to retrieve question

rootRef.child("0").child("questions")
.queryOrderedByChild("id")
.queryEqualToValue(currQuestion)
.observeSingleEventOfType(.Value, withBlock: { (snapshot) in
            for child in snapshot.children {
                print(child.value["question"] as! String)
            }

            }, withCancelBlock: { error in
                print(error.description)
        })

But the above code executes asynchronously. I need to solution to make this synchronous or how to implement listeners so I can fire back the question query once the currQuestion value is changed?

Spark
  • 371
  • 6
  • 15

1 Answers1

8

Write your own method which takes in a completion handler as its parameter and waits for that block of code to finish. Like so:

 func someMethod(completion: (Bool) -> ()){
 rootRef.child("0").child("users")
    .queryOrderedByChild("userId")
    .queryEqualToValue("578ab1a0e9c2389b23a0e870")
    .observeSingleEventOfType(.Value, withBlock: { (snapshot) in

        for child in snapshot.children {
            self.currQuestion = child.value["currentQuestion"] as! Int
        }
        print("Current Question is \(self.currQuestion)")
        completion(true)
        //print(snapshot.value as! Array<AnyObject>)
    }, withCancelBlock : { error in
            print(error.description)
    })
}

And then whenever you want to call that function, call like so:

someMethod{ success in
if success{
//Here currValue is updated. Do what you want.
}
else{
//It is not updated and some error occurred. Do what you want.
}
}

Completion handlers are usually used to wait for a block of code to finish executing completely. P.S. As long as they don't block the main thread, asynchronous requests are made to act synchronous by adding a completion handler like the code shown above.

What it simply does is wait for your currValue to be updated first (receiving the data async from the server) and then when you call someMethod like how I've shown, and since the last and only parameter to the function someMethod is a closure (a.k.a, trailing Closure ), you can skip the parenthesis and call it. Here is a good read about closures. And since the closure is of type (Bool) -> (), you just tell your someMethod when the task is completed which is done like completion(true) in my code, and then while calling it, you call it with success (You can use any word you want) which WILL BE of type Bool as it is declared like so, And then use it in the function call. Hope it helps. :)

Mtoklitz113
  • 3,828
  • 3
  • 21
  • 40
  • Can you explain the second part of the code? Or point me to any promising source that explains completion handler? – Spark Jul 17 '16 at 05:12
  • 1
    I've edited my answer to answer your question. Feel free to ask if you don't understand something. :) – Mtoklitz113 Jul 17 '16 at 05:27
  • So I have a follow-up question. I have an app that initially used plist for it data source which worked seamlessly, due to the main fact that plist data would be fully loaded prior to loading the UICollectionView, as is the nature native to xCode. I now want to utilize Firebase as the data source which is causing issues since the code keeps moving forward while the data is getting pulled from Firebase, so the UIViewController viewdidload function is called prior to retrieved the data so when the view is blank! How do I force the view to not load until the data is retrieved from Firebase first? – Famic Tech Nov 07 '16 at 04:19