Saving a location to a specific child with GeoFire - swift

I am building an app where users can upload a location to a Firebase realtime database using GeoFire. The locations need to be associated with the specific user who uploaded them, and accompany other information input by the user. Right now, the code looks like this:
let key = Auth.auth().currentUser!.uid
let subkey = Int.random(in: 1..<100000)
let object: [String: Any] = [
"Type": type.text!,
"Contact": contact.text!,
"Price": price.text!,
"Availability": availability.text!
]
geoFireRef.child("\(key)").child("\(subkey)").setValue(object)
let location = manager.location!
geoFire.setLocation(location, forKey: "\(subkey)")
All of the data gets uploaded, including the location. All of the data entered by the user goes together into a new child of the user ID. However, the location data goes into a separate child outside of the user ID. The name of the new child is the same random number that is used to name the child containing the other data. However, again, the location data is stored outside of the user ID. Is there a method I can use to get the location data into the same child within the user ID? I've tried things like geoFireRef.child("\(key)").child("\(subkey)").setLocation(location) but such a method does not seem to exist. Is there a method I can use?
example of the database

Basically, what I've decided to do is, instead of using the setLocation method, I will just I declared let latitude = location.latitude and let longitude = location.longitude and uploaded those as values for keys called "latitude" and "longitude" to the database with the other dictionary entries. This works for my purposes.

Related

Firestore is creating Document References instead of appending information to my already specified document

I am currently trying to structure and organize my Firestore Database in my app. Essentially, the first collection of my app will contain my "users" with documents labeled with the email of every user who signs up. I was successfully able to label each document in my "users" database by referencing the Authentication module of Firebase but cannot seem to append data under the individual document paths. I don't know if this is bad practice or not, but this application is for demonstration purposes.
This works successfully in creating an email references in the docs
let db = Firestore.firestore()
db.collection("users").document("\(userEmail)").setData(["firstname":firstname, "lastname":lastname, "uid": result!.user.uid,"email address": email ]) { (error) in
if error != nil {
self.showError("Email has already been used")
This is where I have a problem and the all of the new information is being stored under the a FIRDocumentReference
let iceName1 = iceName1TextField.text!
let iceNumber1 = iceNumber1TextField.text!
let iceName2 = iceName2TextField.text!
let iceNumber2 = iceNumber2TextField.text!
let currentUserEmail = Auth.auth().currentUser!.email
//using this, we can recieve the users email. I don't really know how else to recieve user email so we will need to have someone do that.
let docData: [String : Any] = ["ICE-Name-1" : iceName1 , "ICE-Number-1" : iceNumber1 , "ICE-Name-2" : iceName2, "ICE-Number-2" : iceNumber2]
let userEmail = db.collection("users").document("\(String(describing: currentUserEmail))")
// apparently the document that I am describing above may not exist, therefore, swift is creating document references that are no existent. We need to fix this.
//store data into firebase
let db = Firestore.firestore()
db.collection("users").document("\(userEmail)").setData(docData)
//this appends following information to firebase database
saveLabel.alpha = 1
In the provided code, you are passing a FIRDocumentReference instance as a string to collection.document(). When you stringify an object instance without a description string, it becomes something like <ClassName 0xHexBasedMemoryAddress>.
Try something like this instead:
let userEmailDoc = db.collection("users").document("\(String(describing: currentUserEmail))")
userEmailDoc.setData(docData)

Retriving data from database

First time asking a question here, so sorry if I do it wrong.
Anyways. I'm using Firebase Database to store "Results" in my Quiz app. When data is store it looks like this
Results
-LjQ34gs7QoL1GMufiMsaddclose
Score: xx
UserName: xx
-LjQ3NeCoDGob8wnhstH
Score: xx
UserName: xx
I would like to access score and username from it and display it in a HighScore tableview. Problem is - I can get the "Results" node, but because of the id of the results (ie LjQ34gs7QoL1GMufiMsaddclose) I don't know how to access the score and username.
I got the data snapshot​, but not sure how to "bypass" the id to get to score and username.
Hope I made it at least a bit clear, what the problem is.
let ref = Database.database().reference().child("Results")
ref.observe(.value) { (DataSnapshot) in
print(DataSnapshot.value as Any)
}
You current code gets you a single snapshot with the results of all users. You'll need to loop over the child snapshots to get the result of each user, and then look up their specific properties with childSnapshot(byName:):
let ref = Database.database().reference().child("Results")
ref.observe(.value) { (snapshot) in
for case let userSnapshot as DataSnapshot in snapshot.children {
print(userSnapshot.childSnapshot(forPath: "Score").value)
}
}
Also see:
How to get all child data from firebase without knowing the path
Iterate through nested snapshot children in Firebase using Swift
How do I loop through and get all the keys of the nested nodes in firebase?
Retrieving Data using Firebase Swift
And probably some more from this list.

Query specific nodes from Firebase using Geofire?

How's it going?
I have the following data structure in Firebase:
reports
.XbCacF7tHosOZxxO(child generated by auto-id)
..userId: "XvSjDkLsaA8Se"
..message: "Text goes here"
..latitude: "30.123131"
..longitude: "50.3313131"
.Yoa7HsgTuWm97oP(child generated by auto-id)
..userId: "XvSjDkLsaA8Se"
..message: "Text goes here 2"
..latitude: "30.99999"
..longitude: "50.99999"
users
.XvSjDkLsaA8Se
..name: "John"
..email: "john#email.com"
The idea of the app is that users can make multiple "reports".
In future if I have hundreds of "reports" I don't want it to overload my query. So I'm trying to find a way using Geofire query.
My question is: Is there any way of getting only the "reports" near me using the radius feature from Geofire and the latitude and longitude from "reports"?
Thanks!
I've just found a way of doing that.
I had to save my Geofire node "reports_locations" with the same id generated by Auto Id at the moment of the creation of the report.
The func that my app saves the data on Firebase:
//Save data to Firebase Database
let key = report.childByAutoId().key
report.child(key).setValue(dataToSaveInFirebase)
//Save data to Geofire node
let userLocal = database.child("reports_locations")
let geoRef = GeoFire(firebaseRef: userLocal)
let location = CLLocation(latitude: self.localUser.latitude, longitude: self.localUser.longitude)
geoRef?.setLocation(location, forKey: key)
I hope it can help other people.

Indexing relational data correctly in Firebase Swift

I would like to check that I am indexing my relational data correctly, as I am trying to wrap my head around Firebase. I have a two way relationship between users and addresses, both are one to one. The idea is that the user node is initially created with just their email, then when they add their address, a key for the addressID is added to the user node and simultaneously, a key for the userID is added to the address node. This is meant to be similar to how it is recommended in the "Structure Data" section of the Firebsae docs (at the bottom). However, to identify the address within the user node and to identify the user within the address node, I have used their auto generated key (i.e. address: userAutoID, user: addressAutoID; rather than userAutoID: true and addressAutoID: true; as the former is more descriptive and easier to understand. The use of an autogenerated ID as a key doesnt indicate what the key represents). The JSON structure is as follows (both nodes are children of the top-most node):
As you can see the address possesses the user's ID, and the user possesses the address ID. I feel as though the code I have used to achieve this is protracted and I could be going about this the wrong way. The code is triggered when the user has filled out a form for their address and they press submit. Hence, the User node exists before the address node. I use a for loop to sequentially obtain the key of each address that exists, which i then feed into another query, which then looks to find if the current user's ID exists whithin the current address node as the value of the userID key. If this is true, then I set the ID of the current address to the value of the addressID within the current users associated node. Here is the code:
self.rootRef.child("Addresses").observeSingleEvent(of: .value, with: { snapshot in
for address in snapshot.children {
guard let addressSnapshot = address as? FIRDataSnapshot else {
print("failed to get addressSnapshot")
return
}
let addressSnapshotKey = addressSnapshot.key
self.rootRef.child("Addresses").child(addressSnapshotKey).observeSingleEvent(of: .value, with: { snapshot in
guard let snapshotValue = snapshot.value as? [String: AnyObject] else {
print("error getting snapshotValue")
return
}
let userID = FIRAuth.auth()?.currentUser?.uid
if (snapshotValue["userID"] as! String) == userID {
let correctKey = addressSnapshotKey
let userRef = self.rootRef.child("Users").child(userID!)
userRef.child("addressID").setValue(correctKey)
}
})
}
})
I can see that running two queries from one piece of code, may become expensive when the User and Addresses node become very large. Any feedback would be greatly appreciated!
I would reuse the auto generated User key as the key under Addresses as well.
- Users
- xyz (auto generated)
- email: jo#jo.com
- Addresses
- xyz (use the previously created id here too)
- street
- author
- lat
- lon

Saving and/or Querying User Display Names in Firebase using caseInSensitive?

I'm moving my project over to Firebase from Swift. Firebase user's don't have usernames but I'm allowing them to save a display name which works more like a attribute than an actual object. How can I get users to query for other users/"friends" using case in sensitive text?
You can easily accomplish this task. We don't know how your current data is structured but here's an example
users
user_id_0
dsplay_name: "Smokey"
lower_case_name: "smokey"
user_id_1
display_name: "Bandit"
lower_case_name: "bandit"
When you create a user in Firebase, create a node in the /users node with their uid as the node name, and then display_name and lower_case_name as children
When you write your data to the users node, just lower case the display name when you are writing to the lower_case_name child:
let lowerCaseString = displayNameString.lowercaseString
let userRef = the users uid
let userData = ["display_name": "Bandit", "lower_case_name": lowerCaseString]
userRef.setValue(userData)
Then you query on the lower_case_name child using a lower case string.
ref.queryOrderedByChild("lower_case_name").queryEqualToValue("bandit")
.observeEventType(.ChildAdded, withBlock: { snapshot in
}