geofire-objc icon indicating copy to clipboard operation
geofire-objc copied to clipboard

Geofire iOS - Is there a way to retrieve the full data matching the geo query not just the keys and locations?

Open kesongxie opened this issue 7 years ago • 12 comments

I wonder whether it's possible to get the full data that matches the Geoquery. For example, if I point my Geofire reference to my user and every user has his or her own location. By specifying the radius and center I want, ideally, I want to retrieve some sort of snapshots that contains a list of users given that center and radius instead of just the user ids and the locations information. From the doc here, it only has an example get all the keys(since we are passing user id as the key when we created those geo locations, they will be the key retrieved from the observeEventType method below) that match the query, like so

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

Otherwise, I have a make a query for each user to load their additional information, which is not good for actual practice.

Thanks

kesongxie avatar Oct 15 '17 23:10 kesongxie

Responding late but it will be helpful for others too who are looking for the same. @kesongxie Yes! there is a way by modifying the GeoFire classes. In GFQuery.h there is GFQueryResultBlock which contains two parameters by default NSString *key, CLLocation *location so just add FIRDataSnapshot *snapshot here.

typedef void (^GFQueryResultBlock) (NSString *key, CLLocation *location, FIRDataSnapshot *snapshot);

Run your project and just fix the errors I mean pass this third parameter in this block. For example replace

block(key, info.location); by block(key, info.location, info.snapshot);

They are getting snapshot from Firebase while querying, Just not giving it to you.

TheTiger13 avatar Dec 19 '17 09:12 TheTiger13

@TheTiger13

Hey, I am not familiar with objective C so it's hard for me to understand its code. If you have done this so can you please share your files? Actually there is no property like "snapshot" inside "info". At some points I have data snapshot object so I can pass that to block but at some instance there is nothing like that.

ch-muhammad-adil avatar Mar 13 '18 13:03 ch-muhammad-adil

So I have changed the file GFQuery.m here is the link

and I have changed line number 45 in GFQuery.h from typedef void (^GFQueryResultBlock) (NSString *key, CLLocation *location); to typedef void (^GFQueryResultBlock) (NSString *key, CLLocation *location, FIRDataSnapshot *snapshot);

here is the code I am just using to get end result

     let geofire = GeoFire(firebaseRef: databaseRef.child("locations/"))
       let center = CLLocation(latitude: 31.4598518, longitude: 74.3646121)
       let circleQuery = geofire.query(at: center, withRadius: 5.0)
           _ = circleQuery.observe(.keyEntered) { (key, location,snapshot) in
              // parse the snapshot for your result. 
               print("Key '\(key)' entered the search area and is at location '\(location)'")
           }
           
           circleQuery.observeReady{
               print("All initial data has been loaded and events have been fired for circle query!")
           }

ch-muhammad-adil avatar Mar 17 '18 14:03 ch-muhammad-adil

@ch-muhammad-adil Sorry i was not active here so could not respond to your query. I'm glad to hear that you have done this yourself. Cheers!! 👍

TheTiger13 avatar May 19 '18 04:05 TheTiger13

I find this very useful for my project but is it possible to have dataChanged like there are .keyEntered, keyExited and .keyMoved I need to have snapshot change

nole87 avatar Sep 10 '18 18:09 nole87

Hi @nole87!! GeoFire is for location filter based on radius. I'm not sure about your query why do you need snapshot change event in GeoFire? You can add a different listener for that case.

TheTiger13 avatar Sep 11 '18 06:09 TheTiger13

In realtime DB I also have some data in same place where it's location data, l/0,l/1 and g. In android geofire there are listener with onDataChanged, I need this also for iOS swift. Why? One app feature needs to receive all realtime changes with radius of 10km(it's now adjusted to 10km), so in iOS swift I can adjust firebase realtime db to receive 10km by x or y, but not by x and y, so I try geofire and it works for android, but for iOS swift there is no data change listener. This is traffic issue if app become popular otherwise it's not important but I must prepare for this scenario where app have millions of users, 1 million is enough for market that I want :)

nole87 avatar Sep 11 '18 08:09 nole87

I add new observer and now trying to modify updateLocationInfo in GFQuery.m to make it work well, and I adjust data change observer but it's also fired when data entered, and when data changed,it will work well, best if I disable to fire when data entered, I have:

else if (!isNew && info.isInQuery) { [self.dataChangedObservers enumerateKeysAndObjectsUsingBlock:^(id observerKey, GFQueryResultBlock block, BOOL *stop) { dispatch_async(self.geoFire.callbackQueue, ^{ block(key, info.location,snapshot); }); }]; }

and there is !isNEw but it fire when data is entered ...

nole87 avatar Sep 12 '18 11:09 nole87

@nole87 I have achieved this by doing following changes in GFQuery.h & GFQuery.m classes.

I just defined one more block in GFQuery.h class

typedef void (^GFOnChangeBlock) (FIRDataSnapshot *snapshot, FIRDataEventType eventType);

Then made its property as well.

 /// It will fire when there is any change in node. Observe after ready block to avoid redundancy so that it will call only for new changes.
@property (nonatomic, strong) GFOnChangeBlock onChangeBlock;

Now in GFQuery.m there are 3 mthods:

- (void)childAdded:(FIRDataSnapshot *)snapshot
- (void)childChanged:(FIRDataSnapshot *)snapshot
- (void)childRemoved:(FIRDataSnapshot *)snapshot

These methods call according to their event types so you can use that block here.

    /// In childAdded:
    if (self.onChangeBlock) {
        self.onChangeBlock(snapshot, FIRDataEventTypeChildAdded);
    }
   
    /// In childChanged:
    if (self.onChangeBlock) {
        self.onChangeBlock(snapshot, FIRDataEventTypeChildChanged);
    }

   /// In childRemoved:
    if (self.onChangeBlock) {
        self.onChangeBlock(snapshot, FIRDataEventTypeChildRemoved);
    }

Thats it, Now you will be notify each time there is any change, addition or deletion in this node. If you noticed in every method below method calls as a filter which is preventing extra call of listener.

- (void)updateLocationInfo:(CLLocation *)location
                forKey:(NSString *)key snapshot:(FIRDataSnapshot *)snapshot

I have also uploaded both classes GFQuery.h & GFquery.m. Just replace these with existing files.

And below is Objective-C code how I'm using it:

[query observeReadyWithBlock:^{
        NSLog(@"Ready");
        /// Add your onChangeBlock here
        query.onChangeBlock = ^(FIRDataSnapshot * _Nonnull snapshot, FIRDataEventType eventType) {
            NSLog(@"%@", snapshot.value);
        };
 }];

TheTiger13 avatar Sep 12 '18 13:09 TheTiger13

it works, both solution your and mine :) THANKS! Now I must choose between two solutions :)

nole87 avatar Sep 13 '18 11:09 nole87

I have this....but the error message says "Contextual closure type '(String, CLLocation) -> Void' expects 2 arguments, but 3 were used in closure body"...but if I only take two arguments it says "Ambiguous use of 'observe'".....

let geofire = GeoFire(firebaseRef: querySnapshot!.documents[0].get("koordinaten") as! DatabaseReference)
                    
                    let center = CLLocation(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
                    
                    let circleQuery = geofire.query(at: center, withRadius: 20);
                                   
                    circleQuery.observe(.keyEntered) { (key, location,snapshot) in
                        print("Key '\(key)' entered the search area and is at location '\(location)'")

mark-baumann avatar Feb 02 '21 14:02 mark-baumann

Hi!

I use this code:

geoQuery.observe(.keyEntered) { (key, location, snapshot) in //print("keyEntered")

and this pod: pod 'GeoFire', '~> 4.0.1'

but after pod update or install I change a little bit pod classes in project, write me on [email protected] so I can send you classes and just copy paste it and it will work ...

nole87 avatar Feb 02 '21 14:02 nole87