2

The code that retrieves all locations from Firestore within a 50km location of a given point is given on the Firebase website. Here it is:

// Find cities within 50km of London
let center = CLLocationCoordinate2D(latitude: 51.5074, longitude: 0.1278)
let radiusInM: Double = 50 * 1000

// 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("cities")
        .order(by: "geohash")
        .start(at: [bound.startValue])
        .end(at: [bound.endValue])
}

var matchingDocs = [QueryDocumentSnapshot]()
// 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)
}

The issue that I am having is that matchingDocs (the empty array that the locations from the database are supposed to append to) returns empty every time.

I have double checked that the center and example locations in my database are within 50km of each other. The code is able to retrieve the four documents in my database and I know this because if I put a print statement in the last for loop, I get something printed 4 times.

I need an explanation on exactly what the getDocumentsCompletion function does because I don't understand fully what it does, or how the call works. There are no arguments passed in where the function is called.

I have also added print statements within the getDocumentsCompletion function, but nothing ever gets printed out, so I believe that my issue lies there. I would like an explanation on what exactly is going on there so I can better address the issue.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • For clarity: Are you stating you did not write the `getDocumentsCompletion` function so you have no idea what it does? It would be hard for us to provide any assistance without knowing about that function. How is it being called? Do you know if the snapshot is populated within the closure following the call? Do you have access to that source code? If so, can you include the function in the question? Please update the question with further information and we'll take a look. – Jay Jul 09 '22 at 15:12
  • @Jay Yes I did not write this code. This is the code given by Google on the Firebase website. The link is here:https://firebase.google.com/docs/firestore/solutions/geoqueries#swift_2 I am using the Querey Geohashes feature. I am not sure if the getDocumentsCompletion function is even being called or working properly because any print statements I add to it do not work. I am not sure if the snapshot is being populated, however, queries is acknowledging the four documents I have in my database because if I place a print statement in there, something gets printed out four times. –  Jul 09 '22 at 15:35
  • Ah, I see. So some troubleshooting would be in order to clarify the issue. If the code within `getDocumentsCompletion` is not called at all (as indicated by adding print statements), then the issue lies outside of that. However, you may be having an asynchronous issue since you're unfamiliar with Firebase. How do know `matchingDocs` is nil? e.g. the code in the question 'works' but if it's not implemented properly it may appear to not 'work'. Please review [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve). We need more complete, but brief, code. – Jay Jul 09 '22 at 15:44
  • @Jay I know that matchingDocs is nil because I am printing out matchingDocs at the end and it is printing an empty array. As for further code, I am happy to provide that but what further code is required? All of the code given by Firebase is in my original post, but can also be found here under Query Geohashes: https://firebase.google.com/docs/firestore/solutions/geoqueries#swift_2 If you'd like further code please let me know and I will provide it, but I think this is all that is relevant. getDocumentsCompletion function is also defined in my posted code segment. What does it do? Thanks –  Jul 09 '22 at 15:55
  • 1
    Well, the code as-is will produce a nil result because Firebase calls are asynchronous. If you are **printing at the end** that print call will execute before Firebase can return the results and populate `matchingDocs`. The code provided on the Firebase site (see the comments) state **After all callbacks have executed** and if you're printing before that happens, the output will be nil. But, because the code is just a snippet and not a duplicatable example, we don't know when you're attempting to print the output. I could be totally wrong too! Without seeing a complete example, it's a ? – Jay Jul 09 '22 at 17:00
  • @Jay I think you are right! I forgot about Firebase calls being async. I'll work on that right now. Just to double check, everything except for the first two and last two lines are async correct? –  Jul 09 '22 at 19:50
  • Well, that's hard to say as it depends on perspective. Technically, all of the lines leading up to the code within `for query in queries` loop are executed synchronously. However, they are not executing (like the function) - the call within the loop then runs that function async so at that point the code is waiting on Firebase to return data asynch. Honestly, I *really* dislike that code as it's very hard for a beginner to follow and important code (like when to actually call `for query in queries` and an explanation of when that data is valid would be much more helpful... IMO. – Jay Jul 10 '22 at 13:51
  • 1
    Oh... To make the whole thing about 1000x easier, please check out [GeoFirestore](https://geofirestore.com). I am not a huge fan of external libraries if you can do it yourself but that's a really well done and supported one. – Jay Jul 10 '22 at 13:54
  • @FrustratedDEV111 Please go through the link shared by Jay and let us know if that helped you. – Priyashree Bhadra Jul 11 '22 at 09:56

0 Answers0