I'm trying to build an iOS app in SwiftUI where users can find a "Post" near to their current location. I have a sub collection called Posts with a geohash. Somewhat annoyingly this library by google has been archived https://github.com/firebase/geofire-objc for no reason. Instead I had to use this library https://github.com/emilioschepis/swift-geohash.
I find all the neighboring geohashes around the current user and then run a query against firstore for each geohash starting with geohash and ending with geohash '~'. Here is the function I wrote:
// import https://github.com/emilioschepis/swift-geohash
class FirestorePosts: ObservableObject {
@Published var items = [FirestorePost]() // Reference to our Model
func geoPointQuery(tag:String){
do {
let db = Firestore.firestore().collection("tags")
let docRef = db.document(tag).collection("posts")
// users current location is "gcpu"
let neighbors = try Geohash.neighbors(of: "gcpu", includingCenter: true)
let queries = neighbors.map { bound -> Query in
let end = "\(bound)~"
return docRef
.order(by: "geohash")
.start(at: [bound])
.end(at: [end])
}
func getDocumentsCompletion(snapshot: QuerySnapshot?, error: Error?) -> () {
guard let documents = snapshot?.documents else {
print("Unable to fetch snapshot data. \(String(describing: error))")
return
}
self.items = documents.compactMap { queryDocumentSnapshot -> FirestorePost? in
return try? queryDocumentSnapshot.data(as: FirestorePost.self)
}
}
for query in queries {
print("ran geo query")
query.getDocuments(completion: getDocumentsCompletion)
}
}
catch{
print(error.localizedDescription)
}
}
}
So far the query works and returns items as expected. However, the results are not updated in realtime when there is a change in Firestore.
- How could I make this query update results in realtime? I tried adding
query.addSnapshotListener
instead but it doesn't like "completion:" parameter - How can I ensure that all the queries are finished before returning the results
CodePudding user response:
You're calling query.getDocuments
, which gets data once. If you want to also get updates to that data, you should use addSnapshotListener
which listens for updates after getting the initial docs.
To ensure all queries are finished, you could keep a simple counter that you increase each time your addSnapshotListener
callback is invoked. When the counter is equal to the number of queries, all of them have gotten a response from the server. That's exactly what the geofire-*
libraries for Realtime Database do for their onReady
event.
CodePudding user response:
I refactored to this and it seems to work and updates in realtime. I didn't need to use a counter since Im appending the documents to self.items
(not sure if thats correct though).
...
for query in queries {
query.addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No documents")
return
}
self.items = documents.compactMap { queryDocumentSnapshot -> FirestorePost? in
return try? queryDocumentSnapshot.data(as: FirestorePost.self)
}
}
}