Assemble a List of Users with Geofire/Firebase

Assemble a list of users with Geofire/Firebase

The data from Geofire and the rest of your Firebase Database is not simply "gotten" from the database. It is asynchronously loaded and then continuously synchronized. This changes the flow of your code. This is easiest to see by adding some logging:

func getNearbyTrucks(){
//Query GeoFire for nearby users
//Set up query parameters
let center = CLLocation(latitude: 37.331469, longitude: -122.029825)
let circleQuery = geoFire.queryAtLocation(center, withRadius: 100)

print("Before Geoquery")

circleQuery.observeEventType(GFEventTypeKeyEntered, withBlock: { (key: String!, location: CLLocation!) in
print("In KeyEntered block ")

let newTruck = Truck()
newTruck.id = key
newTruck.currentLocation = location
self.nearbyTrucks.append(newTruck)

}) //End truckQuery

print("After Geoquery")
}

The output of the logging will be in a different order from what you may expect:

Before Geoquery

After Geoquery

In KeyEntered block

In KeyEntered block

...

While the Geo-keys and users are being retrieved from the server, the code continues and getNearbyTrucks() exits before any keys or users are returned.

One common way to deal with this is to change the way you think of your code from "first load the trucks, then print the firs truck" to "whenever the trucks are loaded, print the first one".

In code this translates to:

func getNearbyTrucks(){
//Query GeoFire for nearby users
//Set up query parameters
let center = CLLocation(latitude: 37.331469, longitude: -122.029825)
let circleQuery = geoFire.queryAtLocation(center, withRadius: 100)

circleQuery.observeEventType(GFEventTypeKeyEntered, withBlock: { (key: String!, location: CLLocation!) in

let newTruck = Truck()
newTruck.id = key
newTruck.currentLocation = location
self.nearbyTrucks.append(newTruck)

print(nearbyTrucks[0].id)
}) //End truckQuery

//Execute code once GeoFire is done with its' query!
circleQuery.observeReadyWithBlock({

for truck in self.nearbyTrucks{

ref.childByAppendingPath("users/\(truck.id)").observeEventType(.Value, withBlock: { snapshot in
print(snapshot.value["name"] as! String)

truck.name = snapshot.value["name"] as! String
truck.description = snapshot.value["selfDescription"] as! String
let base64String = snapshot.value["profileImage"] as! String
let decodedData = NSData(base64EncodedString: base64String as String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
truck.photo = UIImage(data: decodedData!)!
})
}

}) //End observeReadyWithBlock
}

I've moved the printing of the first truck into the block for the key entered event. Depending on the actual code you're trying to run, you'll move it into different places.

A more reusable approach is the one the Firebase Database and Geofire themselves use: you pass a block into observeEventType withBlock: and that block contains the code to be run when a key is available. If you apply the same pattern to you method, it'd become:

func getNearbyTrucks(withBlock: (key: String) -> ()){
//Query GeoFire for nearby users
//Set up query parameters
let center = CLLocation(latitude: 37.331469, longitude: -122.029825)
let circleQuery = geoFire.queryAtLocation(center, withRadius: 100)

circleQuery.observeEventType(GFEventTypeKeyEntered, withBlock: { (key: String!, location: CLLocation!) in

let newTruck = Truck()
newTruck.id = key
newTruck.currentLocation = location
self.nearbyTrucks.append(newTruck)

withBlock(nearbyTrucks[0].id)
}) //End truckQuery

//Execute code once GeoFire is done with its' query!
circleQuery.observeReadyWithBlock({

for truck in self.nearbyTrucks{

ref.childByAppendingPath("users/\(truck.id)").observeEventType(.Value, withBlock: { snapshot in
print(snapshot.value["name"] as! String)

truck.name = snapshot.value["name"] as! String
truck.description = snapshot.value["selfDescription"] as! String
let base64String = snapshot.value["profileImage"] as! String
let decodedData = NSData(base64EncodedString: base64String as String, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
truck.photo = UIImage(data: decodedData!)!
})
}

}) //End observeReadyWithBlock
}

Here again, you'll want to move the withBlock() callback to a more suitable place depending on your needs.

Retrieve a list of users based on distance with geofire & firebase

Try the following:

DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("GeoFire");
ref.orderByChild("lattitude").equalTo(lattitude_here).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot datas: dataSnapshot.getChildren()){
String userID=datas.getKey();
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});

Here the snapshot is at GeoFire, then you add a query and iterate to be able to retrieve the ids according to the result of the query.

Retrieving list of users only within 3 miles using geofire

I solved it with the Help of answers from Frank Van Puffelen. Here's what I did

public void onKeyEntered(final String key, GeoLocation location) {

firebaseDatabase.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

Users my=dataSnapshot.getValue(Users.class);
list.add(my);
adapter=new MyAdapter(MainActivity.this,list);
recycler.setAdapter(adapter);

}

getting users on my radius via geofire to a list and rules

The message is pretty explicit:

{
"rules": {
".read": "auth != null",
".write": "auth != null",
"Geofire": {
".indexOn": "g"
}
}
}

GeoFire and Firebase useage to locate nearest user

The problem is, you're referencing the geofire to the root node. You should reference it to a special node for geofire.

What your database structure should look like

"User": {
"uid1": {
"Availability": true,
},
"uid2": {
"Availability": true,
}
},
"User_Location": {
"uid1": {
.. some geofire data ..
},
"uid2": {
.. some geofire data ..
}
}

How to reference the geofire to another location

DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("User_Location");
GeoFire geoFire = new GeoFire(ref);

Then set the location as usual

geoFire.setLocation(userId, new GeoLocation(userLatitude, userLongitude));

After that, the Geofire query should work fine.

GeoFire Query on lists of data

I think you need to separate your GeoFire from your UserDatabase

To Set Locations call

geoFire.setLocation(CLLocation(latitude: 37.7853889, longitude: -122.4056973), forKey: **UserID** )

Then you create the Circle Query

let center = CLLocation(latitude: 37.7832889, longitude: -122.4056973)
var circleQuery = geoFire.queryAtLocation(center, withRadius: 0.6) // 600m

the you make the call against the database

var queryHandle = query.observeEventType(.KeyEntered, withBlock: { (key: String!, location: CLLocation!) in
print("Key '\(key)' entered the search area and is at location '\(location)'")
})

Every Key that is returned will be the UserID for your datebase and you can access the relevant path through a /User/{UserID}/...



Related Topics



Leave a reply



Submit