I have a piece of code inside my Swift built iOS app, to retrieve all the nodes from a Firebase Realtime database. When I execute the code below I've noticed that it does not return all the child nodes.
When I query the particular nodes which are not being returned individually, at first the code returns 'nil' and then on a second attempt retrieves the nodes. (without doing any code changes in the process). Following this process, the node starts to show up in the results with the retrieve all nodes function.
Example 1: First returns nil, then on a second attempt returns the node. Which I can see from the console and definitely exists on the database.
ref?.child("transactions").child(email).child("14526452327").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
print(value)
print("!!****************!!")
// ...
}) { (error) in
print(error.localizedDescription)
}
The following is being used to retrieve all child values; at first this doesn't get all the nodes, however after running the code from Example 1 (twice) it starts to return the node in question.
ref?.child("transactions").child(email).observeSingleEvent(of: .value, with: { (snapshot) in
let childrenCount = snapshot.childrenCount
var counter : Int = 0
for trans in snapshot.children.allObjects as! [DataSnapshot]
{
counter = counter + 1
self.ref?.child("transactions").child(email).child(trans.key).observeSingleEvent(of: .value, with: { (snapshot2) in
I've also checked my Firebase query and data limits and I am nowhere near the threshold for the free account. Any help is greatly appreciated.
Try this:
func getData() {
// Making a reference
let transactionRef = Database.database().reference(withPath: "transactions")
transactionRef.observeSingleEvent(of: .value, with: { (snapshot) in
// Printing the child count
print("There are \(snapshot.childrenCount) children found")
// Checking if the reference has some values
if snapshot.childrenCount > 0 {
// Go through every child
for data in snapshot.children.allObjects as! [DataSnapshot] {
if let data = data.value as? [String: Any] {
// Retrieve the data per child
// Example
let name = data["name"] as? String
let age = data["age"] as? Int
// Print the values for each child or do whatever you want
print("Name: \(name)\nAge: \(age)")
}
}
}
})
}
Related
Is there a way to quickly replace values in a firebase snapshot based on certain criteria. For example, I'm looking to replace all "username" with the value "xyz" where userId = userId_0001 (userId is not unique). This is my code so far:
let databaseRef = Database.database().reference().child("usernames").queryOrdered(byChild: "userId").queryEqual(toValue: "userId_0001")
databaseRef.observe(.value, with: { (snapshot) in
for childSnapshot in snapshot.children {
let username = value?["username"] as? String ?? ""
username.setValue("xyz")
})
}
There are a few issues with the code in the question and the query value in the code (userId_0001) doesn't match the value in the screen shot (userId_001)
From what I gather, you want to query firebase for nodes with a userId of userId_001 (which matches your screenshot) and for those nodes, replace the existing username value with xyz
Here's the code that will do that
func replacer() {
let databaseRef = Database.database().reference().child("usernames").queryOrdered(byChild: "userId").queryEqual(toValue: "userId_0001")
databaseRef.observe(.value, with: { snapshot in
let childSnaps = snapshot.children.allObjects as! [DataSnapshot]
for snap in childSnaps {
snap.ref.child("username").setValue("xyz")
-- or --
snap.ref.updateChildValues(["username": "xyz"])
}
})
}
I includes two options within the for loop. Only use one. The nice thing about updateChildValues is you can replace multiple values within the parent node at once if needed.
I want to achieve this:
This is my current database. It only shows 1 information but it should be showing 3 messages:
// loading the info onto firebase database
let uid = Auth.auth().currentUser?.uid
let ref = Database.database().reference()
ref.child("workout").observeSingleEvent(of: .value, with: { (snapshot) in
print("Got Snapshot")
print(snapshot.childrenCount)
let chilidCount = snapshot.childrenCount
print(chilidCount)
let post:[String:String] = ["\(chilidCount + 1)": textField.text!]
print(post)
ref.child("workout").child(uid!).setValue(post)
})
self.tableView.reloadData()
This is my code so far. I tried looking at other previous question from StackOverflow and also looked at firebase documentation but could not find anything useful.
This is my tableview
Try making a dictionary of the values you want to upload to your FIR Database.
I assume you want to upload the values to your database in a "workout" folder, and in that upload values for each user. You should do the following:
let uid = Auth.auth().currentUser?.uid
let ref = Database.database().reference()
//Reference to the location where the messages get saved to
let userWorkoutRef = ref.child("workout").child(uid!)
userWorkoutRef.observeSingleEvent(of: .value, with: { (snapshot) in
// Get the number of messages
let messagesCount = snapshot.childrenCount
//Making a dictionary: the key is the current number of messages plus one, the value is the current text entered in the text field
let valueToUpload = ["\(messagesCount + 1)": textField.text!]
//Uploading the dictionary to the database
userWorkoutRef.updateChildValues(valueToUpload) { (err, ref) in
if err != nil {
print(err!.localizedDescription)
return
} else {
print("success uploading data to db!")
}
}
}
So let's say I found that a value in database matches value entered by user through query. How can I find the parent of that value?
Ex. Let's say I enter in 35 as the age. 35 is also found in the database so how do I get the parent of that value (0)? Underlined in the picture.
I saw some similar questions asked but I can't seem to find a right answer to my question. Additionally, most of them are in different language.
Here is what I got so far:
#IBAction func onDiagnose(_ sender: Any) {
let ref1 = Database.database().reference(fromURL: "https://agetest.firebaseio.com/")
let databaseRef = ref1.child("data")
databaseRef.queryOrdered(byChild: "age").queryEqual(toValue: Int(ageTextField.text!)).observeSingleEvent(of: .value, with: { (snapshot) in
// if there is data in the snapshot reject the registration else allow it
if (snapshot.value! is NSNull) {
print("NULL")
} else {
//print(snapshot.value)
//get parent
//snapshot.ref.parent?.key! as Any
}
}) { (error) in
print(error.localizedDescription)
}
}
When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
So you need to loop through the child nodes of the resulting snapshot to get at the individual results:
databaseRef.queryOrdered(byChild: "age").queryEqual(toValue: Int(ageTextField.text!)).observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
print(child.key)
}
}) { (error) in
print(error.localizedDescription)
}
Also see listening to value events for lists of data in the Firebase documentation.
My Data in Firebase looks like this:
I am retrieving the data with
var ref: DatabaseReference
ref = Database.database().reference()
ref.child("peopleReminders").child("-LBO0TMbOM0cwd5TMMiP").observe(.value) { snapshot in
for value in snapshot.children {
print(value)
}
}
This prints the following:
Snap (0) r04
Snap (1) r02
Snap (2) r01
This should be an array. What I need to do is get the values R04, R02, and R01 out of it.
You don't need a loop or enumeration to parse it into an array as Firebase supports both Array and Dictionary. You can directly cast the result into an array. Below piece of code will work:
ref.child("peopleReminders").child("-LBO0TMbOM0cwd5TMMiP").observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.exists() {
let result: [String] = snapshot.value as? [String] ?? []
print(result)
}
}
Note: Always check snapshot existence it will be false if data doesn't exist for the requested reference. Use observeSingleEvent when you need to fetch the data for once if you use observe the call back will be call many times whenever there is a change in requested node so that delete, insert, update etc.
I replaced for value in snapshot.children with the following code.
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
print(rest.value!)
}
The new output is now:
R04
R02
R01
So this is my Firebase Structure:
I'm trying to get all books pictures (bookImage), add them to list and then use this list to fill a table or anythings else. (I'm using swift 3)
struct item {
let picture: String!}
var items = [item]()
func getLatestAddedItems(){
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Items").observe(.childAdded, with: {
FIRDataSnapshot in
let picture = (FIRDataSnapshot.value as? NSDictionary)?["bookImage"] as? String ?? ""
//self.items.insert(item(picture: picture), at: 0)
self.items.append(item(picture: picture))
print(self.items[0].picture)
print(self.items[1].picture) // error here
})}
I'm able to see the first print output but on the second one I'm getting fatal error: Index out of range even I have 3 books on my database.
Since your using .childAdded, it iterates through that closure for each object in the data tree, in this case, each book. When you try to print the second picture, its still in its first iteration. Meaning you only have retrieved the first book so far. That's why you can print the first book item but not the second one. If you moved the print statements outside of the closure, and then did the print statements after the closure iterated over all three books, you wouldn't get the error.
Don't change it to .value unless if every time a new one is subsequently added you want to get the entire list of books all over again. If its a large amount of books, it will be a lot of data to go through each time.
Summary: .childAdded gives you one book at a time, with a new snapshot for each one. .value gives you all the books in one snapshot, then you must iterate over them yourself in the closure. ex.
for snap in snapshot.children {
// now you can do something with each individual item
}
also I just noticed your using the FIRDataSnapshot type in your closure, that should be a variable which represents the snapshot you received, not the type itself. Change "FIRDataSnapshot in" to something like "snapshot in" snapshot is a representation of what information was given to you by the observe closure, in this case, an object with a type of FIRDataSnapshot.
Edit:
Your solution you mentioned below works fine, but I'll add an alternative that is cleaner and easier to use.
add an init method to your Book class that takes a FIRDataSnapshot as the init parameter, then init the object when you query Firebase:
struct Book {
let bookImageString: String
init?(snapshot: FIRDataSnapshot) {
guard let snap = snapshot.value as? [String : AnyObject], let urlString = snap["bookImage"] else { return nil }
bookImageString = imageString
{
{
then when you query firebase you can do this:
for snap in snapshot.children {
if let snap = snap as? FIRDataSnapshot, let book = Book(snapshot: snap) {
self.items.append(book)
{
}
doing it this way cleans up the code a little bit and leaves less chance of error in the code.
Also, since your using .value, make sure to empty the data source array at the beginning of the closer, or else you will get duplicates when new books are added.
items.removeAll()
Finally I'm posting the solution:
func getLatestAddedItems(){
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Items").observe(.value, with: {
snapshot in
//self.items.insert(item(picture: picture), at: 0)
for childSnap in snapshot.children.allObjects {
let snap = childSnap as! FIRDataSnapshot
print(snap.key)
let picture = (snap.value as? NSDictionary)?["bookImage"] as? String ?? ""
print(picture)
}
})
}