I'm trying Firebase GeoFire and closures always trip me up. Firebase provides this sample code snippet in their documentation
// After all callbacks have executed, matchingDocs contains the result. Note that this
// sample does not demonstrate how to wait on all callbacks to complete.
for query in queries {
query.getDocuments(completion: getDocumentsCompletion)
}
Unfortunately it doesn't demonstrate just what I need...
I want this to fire on pre set interval so I have a scheduled it like this:
locationTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(getLocations), userInfo: nil, repeats: true)
Then my code is pretty much as per the sample:
var matchingDocs = [QueryDocumentSnapshot]()
@objc func getLocations() {
let center = CLLocationCoordinate2D(latitude: lat, longitude: lon)
let radiusInM: Double = 10000
// Each item in 'bounds' represents a startAt/endAt pair. We have to issue
// a separate query for each pair. There can be up to 9 pairs of bounds
// depending on overlap, but in most cases there are 4.
let queryBounds = GFUtils.queryBounds(forLocation: center,
withRadius: radiusInM)
let queries = queryBounds.map { bound -> Query in
return db.collection("geopoints")
.order(by: "geohash")
}
// Collect all the query results together into a single list
func getDocumentsCompletion(snapshot: QuerySnapshot?, error: Error?) -> () {
guard let documents = snapshot?.documents else {
print("Unable to fetch snapshot data. \(String(describing: error))")
return
}
for document in documents {
let lat = document.data()["lat"] as? Double ?? 0
let lng = document.data()["lng"] as? Double ?? 0
let coordinates = CLLocation(latitude: lat, longitude: lng)
let centerPoint = CLLocation(latitude: center.latitude, longitude: center.longitude)
// We have to filter out a few false positives due to GeoHash accuracy, but most will match
let distance = GFUtils.distance(from: centerPoint, to: coordinates)
if distance <= radiusInM {
matchingDocs.append(document)
}
}
}
// After all callbacks have executed, matchingDocs contains the result. Note that this
// sample does not demonstrate how to wait on all callbacks to complete.
for query in queries {
query.getDocuments(completion: getDocumentsCompletion)
}
}
}
What is the proper way to wait for the callbacks to complete, and continue executing the program outside the getLocations() function with matchingDocs containing all the geopoints? Thanks for any recommendations.
This is my solution. Function allDone() with the completion will be called only after all queries come back.
// button press to read data from Firebase
@IBAction func getData(_ sender: Any) {
getLocations(completion: {
// code here to process all query results
})
}
func getLocations(completion: @escaping () -> Void) {
let center = CLLocationCoordinate2D(latitude: lat, longitude: lon)
let radiusInM: Double = 10000
var queriesFinished = 0
// Each item in 'bounds' represents a startAt/endAt pair. We have to issue
// a separate query for each pair. There can be up to 9 pairs of bounds
// depending on overlap, but in most cases there are 4.
let queryBounds = GFUtils.queryBounds(forLocation: center,
withRadius: radiusInM)
let queries = queryBounds.map { bound -> Query in
return db.collection("geopoints")
.order(by: "geohash")
}
let numOfQueries = queries.count
// Collect all the query results together into a single list
func getDocumentsCompletion(snapshot: QuerySnapshot?, error: Error?) -> () {
guard let documents = snapshot?.documents else {
print("Unable to fetch snapshot data. \(String(describing: error))")
return
}
for document in documents {
let lat = document.data()["lat"] as? Double ?? 0
let lng = document.data()["lng"] as? Double ?? 0
let coordinates = CLLocation(latitude: lat, longitude: lng)
let centerPoint = CLLocation(latitude: center.latitude, longitude: center.longitude)
// We have to filter out a few false positives due to GeoHash accuracy, but most will match
let distance = GFUtils.distance(from: centerPoint, to: coordinates)
if distance <= radiusInM {
matchingDocs.append(document)
}
}
queriesFinished += 1
allDone()
}
// After all callbacks have executed, matchingDocs contains the result. Note that this
// sample does not demonstrate how to wait on all callbacks to complete.
for query in queries {
query.getDocuments(completion:getDocumentsCompletion)
}
func allDone() {
if queriesFinished == numOfQueries {
completion()
}
}
}
I was reading that async/await may be a better way but did not get it to work.