Here's my code:
//Get data from Firebase
func getData(withBlock completion:#escaping() ->Void){
let ref = Database.database().reference().child("hobbies")
let query = ref.queryOrdered(byChild: "cost").queryEqual(toValue: "low")
query.observe(.childAdded, with: {(snapshot) in
self.user_choice_Cost.append((snapshot.childSnapshot(forPath: "hobbyName").value as? String)!)
completion()
//print(self.user_choice_Cost)
})
{ (error) in
print(error)
}
//Manipulating data
getData{
let set2:Set<String> = ["a"]
let set1:Set<String> = Set(self.user_choice_Cost)
print(set1.union(set2))}
This works correctly! But is there any way I can get the user_choice_Cost with all value(["a","b"]) instead of one by one(["a"],["a","b")] and manipulate user_choice_Cost array without putting it inside inside getData{}. Because if I put that outside it will return only "a"
When you observe .childAdded, your completion handler gets called for each individual child that matches your query. If you want to get called only once for all matching children, you should observe .value:
query.observe(.value, with: {(snapshot) in
Since your completion handler gets called only once, the snapshot in this case contains all matching nodes. So you need to loop over snapshot.children:
query.observe(.value, with: {(snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
let value: String = (child.childSnapshot(forPath: "hobbyName").value as? String)!;
user_choice_Cost.append(value)
}
print("value \(user_choice_Cost)")
})
With this code you'll only see one logging output, with all matching hobby names.
Related
I'm trying to fetch the first key from my firebase database but for some reason nothing is being printed out. How can I get the first key from my firebase database using .childAdded
let userMessagesRef = Database.database().reference().child("user-message").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
guard let first = snapshot.children.allObjects.first as? DataSnapshot else { return }
print(first)
This in incredibly easy if you literally are asking how to only ever get the first child of a node. Here's how to only get the first child of a /users node
func getFirstChild() {
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .childAdded, with: { snapshot in
print(snapshot)
})
}
or
print(snapshot.key)
if you just want the key.
Or, if you want to use a query to do the same thing
func getFirstChildAgain() {
let usersRef = self.ref.child("users")
let query = usersRef.queryOrderedByKey().queryLimited(toFirst: 1)
query.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
}
The child_added event is typically used when retrieving a list of items from the database. Unlike value which returns the entire contents of the location, child_added is triggered once for each existing child and then again every time a new child is added to the specified path. The event callback is passed a snapshot containing the new child's data. For ordering purposes, it is also passed a second argument containing the key of the previous child.
From: Read and Write Data on iOS
Per your requirements, this is possible in .value and childAdded.
var child_array = [String:String]
let userMessagesRef = Database.database().reference().child("user-message").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
let value = snapshot.value as? String ?? "Empty String"
let key = snapshot.key
child_array[key] = value;
}) { (error) in
print(error.localizedDescription)
}
then:
if let first = child_array.first?.key {
print(first) // First Key
}
Big NOTE: child_added randomly collects this data, you should never use it to sort your data
I have 2 records in my users table
This code below
let fcmTokenRef = Database.database().reference().root.child("users").child(id!).child("fcmToken")
fcmTokenRef.observe(DataEventType.value, with: { (snapshot) in
print(">>",snapshot)
})
will print out the token of a child
How do I adjust my code to print all the tokens for all my children?
You can try
let fcmTokenRef = Database.database().reference().root.child("users").observe(DataEventType.value, with: { (snapshot) in
print(">>",snapshot)
let dic = snapshot.value as! [String:[String:Any]]
Array(dic.values).forEach {
let str = $0["fcmToken"] as! String
print(str)
}
})
You’re requesting a onetime read, hence you’re reading the data once. You need to use .childAdded
Try this:
let fcmTokenRef = Database.database().reference().child(“users”)
fcmTokenRef.observe(.childAdded, with: { (snapshot) in
print(">>",snapshot)
guard let data = snapshot as? NSDictionary else {return}
var each_token = data[“fcmToken”] as? String
print(“all tokens: \(each_token!)”)
})
#puf says something very important:
differences between child added and value firebase
The child_added event fires for each matching child under the node that you query. If there are no matching children, it will not fire.
I am trying to do a conditional within Firebase Observer.
Essentially I like to check if seat is occupied.
If it is, then orders can be retrieved.
If not then send the restaurant back to the search seat page again.
For some reason, the code within if !taken is never executed even if the condition is met (ie. the owner has inputted the wrong seat number). I have put it within the closure, it should run right?
func retrieveData (){
var taken = false
var seatNumber = "**an Int from other page**"
let refCustomer = Database.database().reference().child("Restaurant").child("Customers")
refCustomer.queryOrdered(byChild: "Seat").queryEqual(toValue: "\(seatNumber)").observeSingleEvent(of: .childAdded, with: { (snapshot) in
if snapshot.exists() {
taken = true
let snapshotValue = snapshot.value as? [String : AnyObject] ?? [:]
self.customerFirstName = snapshotValue["Firstname"] as! String
self.customerLastName = snapshotValue["Lastname"] as! String
self.customerAllergy = snapshotValue["Allergy"] as! String
self.customerID = snapshot.key
self.allergy.text = self.customerAllergy
self.ptname.text = "\(self.customerFirstName) \(self.customerLastName)"
}
if !taken {
print ("oops")
self.performSegue(withIdentifier: "MainPage", sender: self)
}
})
}
There are a number of issues with this code, and possibly your structure so let me see if I can point you in the right direction.
First, you can eliminate the taken varable as it's unneeded. In short
if snapshot.exists() {
//handle the snapshot
} else { //use else here as if the snapshot doesn't exist we want this code to run
print ("oops")
}
Second, ensure your structure is like this
Customers
cust_0
Seat: 1
cust_1
Seat: 2
etc
Third, this is a string of "1"
queryEqual(toValue: "\(seatNumber)")
and you want to query for an Int so make it
queryEqual(toValue: seatNumber)
which queries for an Int of 1
Forth:
When querying Firebase, closures will not execute when .childAdded doesn't find anything. You should use .value.
From the docs of .childAdded
This event is triggered once for each existing child and then again
every time a new child is added to the specified path.
So if no child nodes match the query, it will not execute.
Use this
refCustomer.queryOrdered(byChild: "Seat")
.queryEqual(toValue: seatNumber)
.observeSingleEvent(of: .value, with: { (snapshot) in
And... this is the important part, .value retrieves all nodes that match the query so you will need to iterate over those nodes to work with the child nodes. Assuming there would only ever be one match then you can do this
guard let allChildren = snapshot.children.allObjects as? [DataSnapshot] else {return}
let firstChild = allChildren.first
Fifth. While technically this is ok
let f = snapshotValue["Firstname"] as! String
You are guaranteeing that a Firstname node always exists. If that's true go with it. However, a safer, more Swifty way would be to do this
let f = snapshotValue["Firstname"] as? String ?? "No First Name"
I try to change values for all children in Firebase Database. First I try to collect the IDs, and after that, change value.
ref = DatabaseRef.changeUserRef
ref.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
for items in snapshot.children.allObjects as! [DataSnapshot] {
let itemObject = items.value as? [String: AnyObject]
let id = itemObject?["id"]
self.ref.child(id as! String).child("status").setValue(UserStatus.userOut)
self.ref.child(id as! String).child("inDate").setValue("0000-00-00 00:00")
}
}
})
It works well. All the values are changing as set. But I can't figure out why the app is stuck in this loop. If I try to change any value (like just rewrite in online firebase console), it changes again to UserStatus.userOut's value.
If I close the app, the loop is closed and of course I can change the values.
This
ref.observe(DataEventType.value, with: { (snapshot) in
should be single observe
ref.observeSingleEvent(of: DataEventType.value, with: { (snapshot) in
My database structure looks like this:
Structure
My reference is initialized like this:
let databaseRef = FIRDatabase.database().reference().child("Butikker")
When i run this function in viewdidload, my console prints: nil nil
func fetchNearByPlaces() {
databaseRef.observeSingleEvent(of: FIRDataEventType.value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
print(dictionary["Longtitude"], dictionary["Latitude"])
}
})
I want to print all the values that has the key: Longtitude and Latitude without referring to a specific path.
When observing a node in Firebase by .childAdded, it returns each node, one at a time and the snapshot children can be accessed directly.
When observing by .value however, the snapshot contains the parent nodes and all of the children. In that case, to get at each child node, iterate over the snapshot like this:
let databaseRef = self.ref.child("Butikker")
databaseRef.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let dictionary = snap.value as! [String: Any]
print(dictionary["Longitude"], dictionary["Latitude"])
}
})
also, in Firebase 4, instead of FIRDataEventType.value, just use .value