Home > Blockchain >  Firestore Geohash Query with Live Updating Results in SwiftUI
Firestore Geohash Query with Live Updating Results in SwiftUI

Time:02-01

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.

  1. How could I make this query update results in realtime? I tried adding query.addSnapshotListener instead but it doesn't like "completion:" parameter
  2. 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)
        }
    }
}
  • Related