In the following function I am am performing an initial geofire search query and wish to process all keys found, append them to an array and send the entire array back.
The issue is that the observeReady
block of code is being executed too prematurely and therefore sending an empty array (nothing displays at first load even though there are keys found within the range).
I understand that the observeSingleEvent
call is asynchronous
and may be causing this behaviour, so my question is, how can I manage this and ensure that the keys are processed prior to executing the handler
call within the observeReady
block?
func fetchInitialNearbyVenues(deviceLocation: CLLocation, radius: Double, handler: @escaping ([Venue]) -> ()) {
self.venuesArray.removeAll()
var savedByUsers = [String : String]()
let query = self.GEOFIRE_VENUES_LOC.query(at: deviceLocation, withRadius: radius)
query.observe(.keyEntered) { (key: String!, venueLocation: CLLocation!) in
self.REF_VENUES.child(key).observeSingleEvent(of: .value, with: { (snapshot) in
//process snapshot create and append Venue object to array
//...
//...
self.venuesArray.append(venue) //append Venue to array
})//end observeSingleEvent
}//end geofire query observe
query.observeReady {
handler(self.venuesArray) //PROBLEM: This gets executed prematurely thus sending an empty array via handler
}
}//end func
What you're seeing is expected behavior. The observeReady
is guaranteed to fire after all the corresponding observe(.keyEntered)
have been called. You can verify this with some simple logging statements:
query.observe(.keyEntered) { (key: String!, venueLocation: CLLocation!) in
print(".keyEntered")
}
query.observeReady {
print(".observeReady")
}
When you run this it will print:
.keyEntered
.keyEntered
...
.observeReady
That is in line with how the API is supposed to work. But in the .keyEntered
you are loading additional data from Firebase, which happens asynchronously. And those calls may indeed complete after the .observeReady
has fired.
So you will need to implement the necessary synchronization yourself. A simple way to detect if you have loaded all the additional data, is to keep a count of all the keys for which you still need to load data. So you +1
that every time you add a key, and -1
every time you've loaded the venue data:
let venuesToLoadCount = 0
query.observe(.keyEntered) { (key: String!, venueLocation: CLLocation!) in
venuesToLoadCount = venuesToLoadCount + 1
self.REF_VENUES.child(key).observeSingleEvent(of: .value, with: { (snapshot) in
venuesToLoadCount = venuesToLoadCount - 1
if venuesToLoadCount == 0 {
print("All done")
}
}
}
query.observeReady {
if venuesToLoadCount == 0 {
print("All done")
}
}