Using GeoFire queries in Swift does not give useful output
Asked Answered
S

2

3

I'm pretty desperate right now because I'm trying to use GeoFire on my Firebase Database to find nearby users. I'm pretty much stuck for two days now. I searched Google and stackoverflow a lot and tried everything I found there but without any success. Now my last hope is to create this thread myself and hope someone can help me.

My database pretty much looks like this:

users
    user_id1
        email: [email protected]
        username: xxx
    user_id2
        email: [email protected]
        username: yyy
users_locations
    user_id1
        location
            g: xxxx
            l
                0: 30.0000
                1: 5.0000
    user_id2
        location
            g: yyyy
            l
                0: 30.0010
                1: 5.0010

I don't know if it is necessary to save the location data seperated from the users but I also tried to save it like this with the same results so I think it doesn't matter:

users
    user_id1
        email: [email protected]
        username: xxx
        location
            g: xxx
            l
                0: 30.0000
                1: 5.0000

Now the code I'm using:

To write the location into the database I use the following, which is working fine:

let user = FIRAuth.auth()?.currentUser
let uid = user?.uid

let ref = FIRDatabase.database().reference()
let geofireRef = ref.child("users_locations")
let geoFire = GeoFire(firebaseRef: geofireRef.child(uid!))

geoFire?.setLocation(myLocation, forKey: "location")

Now I try to use a GeoFire Query to show me all users in a defined radius which is printing nothing for this one:

let ref = FIRDatabase.database().reference()
let geofireRef = ref.child("users_location")
let geoFire = GeoFire(firebaseRef: geofireRef)

let circleQuery = geoFire?.query(at: center, withRadius: 10)
circleQuery?.observe(.keyEntered, with: { (key: String?, location: CLLocation?) in
         print("Key '\(key!)' entered the search are and is at location '\(location!)'")
    })

I figured out that I get a result if I go to my actual uid child. Then it print my actual users location and key but that's of course not what I want to have.

let geoFire = GeoFire(firebaseRef: geofireRef.child(uid!))

So I want GeoFire to search through my uid's in 'users_locations' and return me all uid's which are in my defined radius.

To me, it seems like it is only searching for the child named 'location' in the child defined in my Reference (users_location -> user_uid) and if I try to query over 'users_location', I get nothing.

What am I doing wrong? How can I make the query search reference child and give me the uid back?

Shimberg answered 15/3, 2017 at 12:41 Comment(4)
Where are you defining center?Generalist
I set center to my actual location before. I tried a little bit but in this example it is my locationShimberg
And are the locations in users_locations within 10 kilometers of that center? Because otherwise they won't show up.Generalist
They are within 1 kilometer. I get the same result even if they are the exact same and I put in 1000 for the radius.Shimberg
G
2

Try writing the locations with GeoFire like this. You don't need to store them under that location key.

let ref = FIRDatabase.database().reference()
let geofireRef = ref.child("users_locations")
let geoFire = GeoFire(firebaseRef: geofireRef)

geoFire?.setLocation(myLocation, forKey: uid!)
Generalist answered 15/3, 2017 at 13:16 Comment(1)
It seems to work! Thank you very much for the quick solution.Shimberg
T
4

When using Geofire, you have two lists of data:

  1. the list of objects, in your case users
  2. the list of locations for those objects, which is maintained and queries through Geofire

The two lists are indeed meant to be separate. From the Geofire documentation (emphasis mine):

Assume you are building an app to rate bars and you store all information for a bar, e.g. name, business hours and price range, at /bars/<bar-id>. Later, you want to add the possibility for users to search for bars in their vicinity. This is where GeoFire comes in.

You can store the location for each bar using GeoFire, using the bar IDs as GeoFire keys. GeoFire then allows you to easily query which bar IDs (the keys) are nearby. To display any additional information about the bars, you can load the information for each bar returned by the query at /bars/<bar-id>.

I highlighted the two most important bits for your questions:

  1. the items in the list of users and the list of their locations should use the same key (this is what Andrew also pointed out in his answer). By using the same key, you can easily look up the user for a location and vice versa.
  2. you will need to separately load the user for each key within your query.

Andrew shows how to properly write the locations for each user. What is left then is to load the additional information about each user that is in range of the result. You'd do this in he .keyEntered handler:

let usersRef = ref.child("users")

let circleQuery = geoFire?.query(at: center, withRadius: 10)
circleQuery?.observe(.keyEntered, with: { (key: String?, location: CLLocation?) in

     print("Key '\(key!)' entered the search are and is at location '\(location!)'")
     // Load the user for the key
     let userRef = usersRef.child(key)
     userRef.observeSingleEvent(FIRDataEventType.value, with: { (snapshot) in
         let userDict = snapshot.value as? [String : AnyObject] ?? [:]
         // ...
     })
})
Thermion answered 15/3, 2017 at 13:31 Comment(3)
Question for you Puf, I remember seeing you in a YT video geoquerying documents around state capitals and I think you said you were doing all of that with Firestore. Is that correct? Can Firestore users, therefore, geoquery using Geofire the same way Firebase users do?Peccary
The video is here: youtube.com/watch?v=mx1mMdHBi5Q. As shown there, you can use the same approach on both Realtime Database and Cloud Firestore. But there is only a pre-built library for RTDB. For Firestore you'll have to copy/paste parts of the library, as I do in this video.Thermion
Don't know if enough people tell you this Puf but you are the man.Peccary
G
2

Try writing the locations with GeoFire like this. You don't need to store them under that location key.

let ref = FIRDatabase.database().reference()
let geofireRef = ref.child("users_locations")
let geoFire = GeoFire(firebaseRef: geofireRef)

geoFire?.setLocation(myLocation, forKey: uid!)
Generalist answered 15/3, 2017 at 13:16 Comment(1)
It seems to work! Thank you very much for the quick solution.Shimberg

© 2022 - 2024 — McMap. All rights reserved.