Load existing Geofire location if one exists - ios

I would like Geofire to do one of two things; 1 - Look for a venue location in Firebase and if none exists, 2 - Create one.
So to clarify:
User creates a post based on the current venue.
Geofire checks to see if it already has a venue (Long and Lat cocoordinates) on record for that geolocation. If it DOES have that location stored in Firebase then it takes the user's new post and ADDS it to the same venue.
If Geofire DOES NOT detect a location in Firebase it creates a new venue and attaches the post to it.
I have separated my venue details from my Geofire coordinates but I dont understand how do I get Geofire to differentiate between whether an entry for this location does or does not exist within Firebase.
At the moment when I run my app it creates a new entry every time as per the screen-grab below, which is obviously not what I want.
Here is the code:
First I set the reference for the name and the directory (I set them seperately because I use the name ref in a few instances)
override func viewDidLoad() {
super.viewDidLoad()
geoFireNameRef = Database.database().reference().childByAutoId()
geoFireRef = Database.database().reference().child("PostLocations").child("Coordinates")
geoFire = GeoFire (firebaseRef: geoFireRef)
}
and then in my post method I call:
func savePost () {
let location = CLLocation.init(latitude: lat!, longitude: long!) //Get Coordinates
geoFire.setLocation(location, forKey: self.geoFireNameRef.key)// Save Coordinates
postLocationDB.child(self.geoFireNameRef.key).setValue(locationDictionary) // Save Venue Details
}

So I understand that you want to check if there is a venue with an existing couple of coordinates. What I would suggest is to store this couple under your Coordinates/{venueId} node as an extra field which is composed by the two coordinates, like the following:
- Coordinates
- -LAWR2LH.....
- l
- 0: 22.345
- 1: 22.345
- compoundL: "22.345-22.345"
and then directly query the Real Time Database with a query that combines queryOrderedByChild and queryEqualToValue in order to find if a Coordinates/Venue node with compoundL = 22.345-22.345 is already existing.

Related

How to observe a node's child items based on a variable of that child node in Firebase

I am creating a messaging app, and using Firebase as backend. I know how to observe for added children of a given node using observe method and .childAdded. my data is structured as
Node
ChildNode1
Key1:value1
Key2:value2
Key3:value3
timestamp:Int
ChildNode2
.
.
.
is there a way of observing the parent node but only retrieve childnodes that have a timestamp value greater than a particular value? so that I won't need to retrieve all the child nodes and only keep the ones that meet the requirements?
This is the code I use to retrieve the childnodes:
let userMessagesRef = FIRDatabase.database().reference().child("DataStructure").child("ListOfItems").child("ParentNode")
observerHandler = userMessagesRef.observe(.childAdded, with: { (snapshot) in
//My code would go here
//This works as required AFTER all the data is downloaded
//In other words, once the previous (not needed) childnodes and those I want are downloaded
//I can use it for subsequent and newly added childnodes
//However, I don't want to retrieve all the childnodes everytime this runs
//I want to only retrieve childnodes that were added after a particular time
//I save the timestamp as Int of seconds in the childnode.
//I also save the timestamp of last retrieve childnode in the app
//What I ideally want is to retrieve all childnodes after the timestamp
//saved in the app (something like any childnode with timestamp greater than the timestamp int in the code)
}, withCancel: nil)
Would it work if I add the observer to a query, and set the query to retrieve the childnodes with timestamp after my local timestamp I saved?

Firebase: How expensive is it to observe a lots of nodes?

I am writing an iOS app where I will have to observe a node for every contact a user has. E.g. this means observing up to 30 different nodes.
How expensive is this regarding the traffic that is caued by lots of observations instead of one big observation?
EDIT:
Let's assume every registered user adds new messages to:
- messages
- $userId
- $messageId
- timestamp
- text
Now a logged in user wants to retrieve the messages of all of his contacts (much like twitter):
let contactIds = [userId1, userId2, ...] // Array of Strings
for userId in contactIds {
let messagesRef = self.ref.child("messages").child(userId).observe.queryLimitedToLast(100)
messagesRef.observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
// ...
})
}
Since the app shouldn't support followers / following like twitter, this cannot be solved like the firefeed.io example, where every user only observes one single feed node where each one of his contacts writes to.

Firebase number of results that contain a value - iOS Swift

I have a fairly simple scenario whereby I am trying to get the number of users with a particular favorite color. For example, I would like to retreive the number of users that have a favorite color of 'Blue'.
The following code will get me the number of children nodes each user has which in this case is 4 (favoriteColor, displayName, email and provider). I would instead like to get the number of users that have a particular favorite color.
let ref = Firebase(url: "https://project.firebaseio.com/users")
ref.queryOrderedByChild("favoriteColor").queryEqualToValue("Blue").observeEventType(.ChildAdded, withBlock: { snapshot in
print(snapshot.childrenCount)
})
I am trying to keep a live count of the number of users with a particular favorite color via UILabel so I will update the label each time there is a change to the number of results.
Is there currently a way to do this?
I solved this by changing .ChildAdded to .Value.
I also changed observeEventType to observeSingleEventOfType
let ref = Firebase(url: "https://project.firebaseio.com/users")
ref.queryOrderedByChild("favoriteColor").queryEqualToValue("Blue").observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot.childrenCount)
})
Hope this helps someone in future!

How to save GeoFire with Firebase properties in Swift?

I am trying to save an object that contains a name string, an address string and location coordinates. In the docs it seems that there is one way to save firebase data and another way to save GeoFire data.
Firebase:
var alanisawesome = ["full_name": "Alan Turing", "date_of_birth": "June 23, 1912"]
var gracehop = ["full_name": "Grace Hopper", "date_of_birth": "December 9, 1906"]
var usersRef = ref.childByAppendingPath("users")
var users = ["alanisawesome": alanisawesome, "gracehop": gracehop]
usersRef.setValue(users)
GeoFire:
let geofireRef = Firebase(url: "https://<your-firebase>.firebaseio.com/")
let geoFire = GeoFire(firebaseRef: geofireRef)
geoFire.setLocation(CLLocation(latitude: 37.7853889, longitude: -122.4056973), forKey: "firebase-hq") { (error) in
if (error != nil) {
println("An error occured: \(error)")
} else {
println("Saved location successfully!")
}
}
Is it possible to save both location and other data in the same request? I'd like to do it in the same request because I don't want the user to be able to create an object without location data if the location request fails. Or is there another smart way to impose that restriction?
Since writing the original entity and its geolocation are two separate calls, they will be two separate write operations. One of these writes is done by your code, the other is done by GeoFire. Both calls write to different parts of the JSON tree.
Firebase recently added the ability to write to multiple locations with a single update() call. With that you could write both the geolocation and the entity in one call. If you want to do that, you'll have to change GeoFire to allow for it.
I also struggled for while finding a solution to this, I ended up taking an unorthodox approach. I make my own key title for the GeoFire key entered. Almost like a vin for a car. The first character is a number defining my annotation to use. Next set of numbers is date generated at time of post, after the date is a string of user data because it doesn't matter how long that text is. And that goes as the key. In my maps app the title key is then called and I retrieve the title as a string and cut it up. There are certain characters that are not allowed to be in the GeoFire key title, so you have to restrict the user from typing those.

How to show places like "national parks" using google maps sdk in iOS?

I'm using xcode 6.4, Swift and iOS8. My goal is to show a map using the google maps (and places?) sdk. This map should contain all results google delivers when i search for "national park" on google maps.
What I already did is the following:
Create an API Key for iOS at google (places and maps sdk)
Show the map and the users current position
Now I'm struggling in showing the parks when the user opens the map. I already dived around in googles sdk documentation, and came to the conclusion that I'll need the Places SDK for that. But now I'm a bit lost, all I find are scenarios in which the user either has to choose between different "place types" or the app is calling information for one particular place, knowing its id.
After you have the key and every thing you can get places by calling the map api like this. the parameters (format strings are self explanatory) you have to tell the center location by giving latitude longitude (you can get it from users location if you want to show surrounding places). distance is the area for which you want to query for places. types this mention types of places so it canbe parks, restaurants etc.
When you have run the query you will get a list of places with details and the you have to draw using map annotations
var googleURLString NSString(format:#"https://maps.googleapis.com/maps/api/place/search/json?location=%f,%f&radius=%#&types=%#&sensor=true&key=%#",latitude, longitute, distance, type, apiKey ) as String
let googleURL = NSURL(string: googleURLString)
let locationData = NSData(contentsOfURL: googleURL!, options: nil, error: nil)
let dataToUse = NSJSONSerialization.JSONObjectWithData(locationData, options: NSJSONReadingOptions.AllowFragments, error:&error) as NSDictionary
let locations = dataToUse["results"]

Resources