Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
kesongxie opened this issue Oct 15, 2017 · 12 comments

Comments

@kesongxie
Copy link

kesongxie commented Oct 15, 2017

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

@TheTiger13
Copy link

TheTiger13 commented Dec 19, 2017

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.

@ch-muhammad-adil
Copy link

ch-muhammad-adil commented Mar 13, 2018

@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
Copy link

ch-muhammad-adil commented Mar 17, 2018

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!")
           }

@TheTiger13
Copy link

TheTiger13 commented May 19, 2018

@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!! 👍

@nole87
Copy link

nole87 commented Sep 10, 2018

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

@TheTiger13
Copy link

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.

@nole87
Copy link

nole87 commented Sep 11, 2018

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
Copy link

nole87 commented Sep 12, 2018

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 ...

@TheTiger13
Copy link

TheTiger13 commented Sep 12, 2018

@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);
        };
 }];

@nole87
Copy link

nole87 commented Sep 13, 2018

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

morganchen12 added a commit that referenced this issue Oct 14, 2019
#101 Implementation. Add observers with snapshots.
@mark-baumann
Copy link

mark-baumann commented Feb 2, 2021

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)'")

@nole87
Copy link

nole87 commented Feb 2, 2021

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 ivankovic87@gmail.com so I can send you classes and just copy paste it and it will work ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants