ios firebase database get key of value - ios

Sorry newbie here. I can't seem to find any solution that helps me get the following information from the firebase realtime database (see image)
orange rectangle marks structure of data and data to be retrieve
This is my current code
ref.child("locations").observe(.value, with: { snapshot in
for child in snapshot.children{
let valueD = child as! DataSnapshot
let keyD = valueD.key
let value1 = valueD.value
print(value1)
// This gives "-L-other letters" = 0 (but I only want the string without "= 0")
})
Is there any way I can do this? Thanks!

If locations is the root of what you show in the screenshot, you're only looping over the first level of children (37d42... etc). To get the keys you marked, you need to loop one level deeper. So:
ref.child("locations").observe(.value, with: { snapshot in
for child in snapshot.children{
for grandchild in child.children{
let valueD = grandchild as! DataSnapshot
let keyD = valueD.key
let value1 = valueD.value
print(keyD)
}
}
})

Related

Swift -Firebase is it possible to Post and Observe at same time

When I want to observe I run:
let likesRef = Database.database().reference().child("likes").child(postId)
likesRef.observeSingleEvent(of: .value, with:{ (snapshot) in
let likes = snapshot.childrenCount
let totalLikes = Int(likes)
// display totalLikes
})
When I want to post I run:
let dict = [uid: 1]
let likesRef = Database.database().reference().child("likes").child(postId)
likesRef.updateChildValues(dict) { [weak self](error, ref) in
if let error = error { return }
// if no error then update the ref
})
When posting the completionBlock has 2 values:
withCompletionBlock: (Error?, DatabaseReference)
Is there a way that I can also observe the ref that I am posting to using the 2nd completionBlock value: DatabaseReference:
For eg once the user likes something I want to update the ref and display the new number of likes at the same time without having to run observeSingleEvent:
let dict = [uid: 1]
let likesRef = Database.database().reference().child("likes").child(postId)
likesRef.updateChildValues(dict) { (error, ref) in
if let error = error { return }
// *** somehow get the total amount of likes from the ref ***
// If possible I want to avoid this code below
likesRef.observeSingleEvent(of: .value, with:{ (snapshot) in
let likes = snapshot.childrenCount
let totalLikes = Int(likes)
// display totalLikes
})
})
Actually its not possible to get both Event at same time. Once you like once your data reach at firebase database then you will get Value,Change,ChieldAdded event on App side. It will take 1-2 second or less depend on Internet. So Post will call first and then observer will call.
You can get observer this by two ways:
Step 1 : Add Child Change listener
let likesRef = Database.database().reference().child("likes").child(postId)
likesRef.observe(.childChanged, with: {
(snapshot) in
let likes = snapshot.childrenCount
let totalLikes = Double(Int(likes))
// display likes
})
Child change listener listens changes on snapshot
Step 2 : Put value listener
let likesRef = Database.database().reference().child("likes").child(postId)
likesRef.observe(.value, with: {
(snapshot) in
let likes = snapshot.childrenCount
let totalLikes = Double(Int(likes))
// display likes
})
Value listener listens every time
I Hope this will help...
Here's some very abbreviated code to accomplish the task of adding a users uid who likes a post and incrementing a counter via runTransactionBlock. Keep in mind there are 100 ways to do this.
A proposed structure
all_posts
post_0
like_count: 2
post: "Hello, World"
likes
post_0
uid_0: true
uid_1: true
Based on the question and discussion, I broke the posts and likes into separate nodes. The reasoning is that when the posts are loaded, they will probably be displayed in a list and all that's really needed at that point is the post topic and the total number of likes, so I made 'like_count' part of the post node.
If we added the actual likes within the post node itself, it could be 10,000 or 10,000,000 and there's no reason to load all of that data when loading in the posts. Having that many likes could also overwhelm the device.
Within the likes node, each post key references the post in the all_posts node. The children are the uid's of the users that liked the post and the value is a boolean 'true' as a placeholder. Nodes with no values cannot exist so 'true' keeps it in place and is a tiny amount of data.
Then the code -
func updatePostWithLikesViaTransaction() {
let postsRef = self.ref.child("all_posts") //self.ref points to my Firebase
let thisPostRef = postsRef.child("post_0")
thisPostRef.runTransactionBlock ({ (currentData: MutableData) -> TransactionResult in
if var data = currentData.value as? [String: Any] {
var count = data["like_count"] as? Int ?? 0
count += 1
data["like_count"] = count
currentData.value = data
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
return
}
print("successfully incremented counter")
let likesRef = self.ref.child("likes")
let likesPostRef = likesRef.child(thisPostRef.key!)
likesPostRef.child("uid_2").setValue(true)
}
}
The above is not ideal by any means but demonstrates the process (and works with the presented structure).
First we get a reference to the post we want to update and then within a transaction block read the currentData 'like_count' child which would return 2 based on the values in the structure. That's incremented to 3, updated within currentData and then updated via the TransactionResult.
runTransactionBlock also has an optional completion callback and we are using that to, upon success, update the post within the likes with the users uid and true.
The end result of running this is that the counter is incremeted to 3 and 'uid_2: true' is added to the 'likes/post_0' node

Swift and Firebase sort out drivers into an array first

I'm trying to retrieve all available drivers from my Firebase Database, then maybe put them in an array, So I can Calculate which driver is closer to a user. Or is there a better way of doing it than Array
Here is my Code Below for Retrieving the drivers, Its working and Below is the image for my database table
ref.child("drivers").queryOrdered(byChild: "status").queryEqual(toValue: "available").observe(.childAdded) { (snapshot) in
print(snapshot.value!)
}
Here is a Code that would sort out the closest driver to a user but I need to sort out drivers into an array first
var closestLocation: CLLocation?
var smallestDistance: CLLocationDistance?
for location in locations {
let distance = currentLocation.distanceFromLocation(location)
if smallestDistance == nil || distance < smallestDistance {
closestLocation = location
smallestDistance = distance
}
}
print("smallestDistance = \(smallestDistance)")
To populate an array from Firebase, we need to take each driver node, obtain the child nodes we are interested in, group them up and add them to an array.
To do that we are going to take the presented Snapshot, map it to a Dictionary and then read the child nodes by their keys. Then leverage a Structure to hold the distance and drivers name in an array.
Keep in mind that using .childAdded will iterate over all of the existing nodes once, and then call the code in the closure any time a new child is added - you may want to consider reading the node once with .value and iterate over the nodes in that snapshot instead (i.e. .observeSingleEvent(of: .value))
Here's some very verbose code to read your drivers node and add them, and their distance from our current location into an array.
Please be aware that Firebase is asynchronous so processing the array distances should be done within the closure and any code following this code will run before this code completes.
struct DriverAndDistance {
var driver = ""
var distance = 0.0
}
var driverArray = [DriverAndDistance]()
func readDriversAndAddObserver() {
let driversRef = self.ref.child("drivers")
driversRef.observe(.childAdded, with: { snapshot in
if snapshot.exists() == false { return }
let dict = snapshot.value as! [String: Any]
let firstName = dict["Firstname"] as! String
let long = dict["long"] as! Double
let lat = dict["lat"] as! Double
let distanceFromHere = self.getDistance(long, lat)
let dd = DriverAndDistance(driver: firstName, distance: distanceFromHere)
self.driverArray.append(dd)
//do calculations here and present data to user
})
}

iOS Firebase get data where value equals to something

How can I get data in swift3 from firebase where the value equals something.
In MySQL that would be "SELECT * FROM users WHERE type = 'brown'"; how can I write that where part in Firebase?
I have this DB
And I want to get all the data from it where month == currentMonth (I have a currentMonth variable)
You can Query like this:
let ref = Database.database().reference()
ref.child("Budget")
.queryOrdered(byChild: "Month")
.queryEqual(toValue: "November")
.observe(.value, with: { (snapshot: DataSnapshot) in
var array:[Budget] = []
for budgetChild in snapshot.children {
let budgetSnapshot = budgetChild as! DataSnapshot
let budgetDictionary = budgetSnapshot.value as! [String:String]
let budget: Budget = Budget()
budget.description_ = budgetDictionary["Description"]
budget.month = budgetDictionary["Month"]
budget.type = budgetDictionary["Type"]
budget.value = budgetDictionary["Value"]
array.append(budget)
}
})
As far as I'm aware firebase realtime database doesn't have support for querying like this. Instead you need to call down the whole ref 'budget' then filter it locally in your app.
FireStore apparently has better query support although its in Beta.

Firebase queryEquals Not Returning Values in Swift

I have a Firebase structure that looks like:
- key1
-key2
-key3:value
-key4:value
-key5:value
-key6:value
I am looking to return records where key3's value equals a particular string.
let ref = FIRDatabase.database().reference()
let path = "Object/key1/key2"
ref.child(path).queryOrdered(byChild: "key3").queryEqual(toValue: "myvalue").observeSingleEvent(of: .value, with: {(snapshot) in
The above returns NULL. If I remove the queryEqual, all records are returned. I have triple checked that the "myvalue" is matching in FB as well as in the call.
I have the FB call in a loop as key1 and key2 change. In the end I only want those records (everything under key2) returned only if key3 is equal to value I send in my queryEqual statement.
To give the full return see below. I commented out my actual return process to just check via a simple print:
ref.child(path).queryOrdered(byChild: "key3").queryEqual(toValue: "mvValue").observeSingleEvent(of: .value, with: {(snapshot) in
for test in snapshot.children
{
print (test)
}
/*if let returnDict = snapshot.value as? NSDictionary
{
helperProtocol.firebaseDidReturn?(returnDict: returnDict, extraDict: extraDict, name:name)
}
else if let returnArray = snapshot.value as? NSArray
{
helperProtocol.firebaseDidReturn?(returnDict: ["array":returnArray], extraDict: extraDict, name:name)
}
else
{
helperProtocol.firebaseDidReturn?(returnDict: nil, extraDict: extraDict, name:name)
}*/
})
The issue was the depth of the query path. From the above I was using:
let path = "Object/key1/key2"
The path needed to be:
let path = "Object/key1"
So even though the search key (key3) is under key2, the approach is to go one level higher as shown above.

Setting a class's values with multiple queries

I've got a class that has Title, users and a timestamp.
My database is set up like :
users
ablksjdlf39493
rooms
room1: true
rooms
room1
timestampOfLastPost: 39403942
users
039403fsjlkj: true
;alkjsdksdkj: true
I'm running a query on my user's rooms and grabbing the snapshot.key to get in this example "room1". Then inside of the first query I run another query and I use that key as a child reference to my rooms node to go to that room (rooms-->room1), and set is as my class object's title. From the 2nd query I'm pulling out the timestamp and use that for my object's timestamp.
I'm a bit confused how to get the childcount of out users.
I was running yet another nested query inside of my second query. But then I was getting all sorts of weird duplicate posts being added to my tableview when I was loading them. And that just doesn't seem very efficient anyways.
Is there a way in my second query to get both the timestamp and the children.count of my users node? The only way I've seen to do that is to run an observer on the specific node and do a snapshot.childrenCount.
edit:
currentUserFirebaseReference.child("rooms").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
self.interestsArray = []
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
let eachInterest = Interest()
let interestKey = snap.key
let title = snap.key.uppercaseString
eachInterest.title = title
//grabbing the user's current rooms and getting the snap.key to use for my next query.
DataService.ds.REF_INTERESTS.child(interestKey).observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
if let lastuser = snapshot.value!["lastJoin"] as? Int {
eachInterest.latestUser = lastuser
} else {
print("couln't pull that value")
}
//getting the timestamp
DataService.ds.REF_INTERESTS.child(interestKey).child("users").observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
// I can get the users children.count by doing yet another query, but my
// interestsArray gets all messed up and I get duplicates / weird
// loading in my tableview. So I'm looking to not do this whole
// separate query and just grab it from up above where I get the
// timestamp?
let snapshotChildren = String(Int(snapshot.childrenCount))
eachInterest.users = snapshotChildren
self.interestsArray.append(eachInterest)
//self.interestsArray.sortInPlace({ $0.latestUser < $1.latestUser })
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
})
})
}
}
}
}

Resources