I'm new to setting up firebase's realtime database, and I'm trying to access the content from a message to display in the app.
Here is how my database is structured:
I want to access "content" for each of the messages with the fewest value for "numberofresponses."
let ref: DatabaseReference! = Database.database().reference(withPath: "messagepool")
ref.queryOrdered(byChild: "numberofresponses").observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() {
print("no snapshot exists")
return }
print(snapshot)
The above code correctly prints the "messagepool," but I want the specific content value from each of the branches. I seem to be missing something. What is the correct way to do this? Thanks
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 in your closure/completion handler you will need to loop over snapshot.children to get at the individual result snapshot(s):
ref.queryOrdered(byChild: "numberofresponses").observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.exists() {
print("no snapshot exists")
return
}
for childSnapshot in querySnapShot.children.allObjects as! [DataSnapshot] {
print(childSnapshot.key)
print(childSnapshot.value)
guard let value = childSnapshot.value as? [String: Any] else { return }
do {
guard let content = value["content"] as? String,
...
}
}
}
Also see:
Firebase queryOrderedByChild() method not giving sorted data
Retrieving Data using Firebase Swift
Getting nil in parsing Firebase values swift using Codable and CodableFirebase
Related
For a few days I have been trying to read my data from firebase without success.
Indeed it is a set of tables also containing tables.
This function is to retrieve the subjects and at the same time the paragraphs
func getSubjects() {
let subjectRef = database.child("subjects")
subjectRef.observe(.childAdded, with: { (snapshot) in
for child in snapshot.children {
print(snapshot)
if let snapshot = child as? DataSnapshot,
let subject = Subject(snapshot.value)
//subjectList.append(subject)
// print("Data : \(subject)")
}
})
}
This is the firebase screen
Console screen
On Android I didn't have this problem, but since I'm new to iOS, I'm having a hard time coping.
Any help will be welcome. Thank you
At the moment, you are observing the database for constant changes and it will only run when a child/value has been added into the place you're currently checking, for this you may only want to retrieve a value once, and every time that view is loaded then it will fetch from the database again. It's a lot more efficient and less costly. You may want something like this:
ref = Database.database().reference()
ref.child("subjects").child("0").child("paragraphs").child("0").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let location = value["location"] as? NSDictionary
let title= value?["title"] as? String ?? ""
let text = value?["text"] as? String ?? ""
let latitude = location?["latitude"] as? String ?? ""
let longitude = location?["longitude "] as? String ?? ""
}) { (error) in
print(error.localizedDescription)
}
You think each child with nodes inside it as an array, or a json object. You can cast them into an NSDictionary and use that cast to access values inside them if they're nested.
If they're not nested and in the same level as the place you're watching in the database ref, like for instance, above we are looking in the subjects > 0 > paragraphs > 0 node within the database. Title is a value inside that node and not a child so we can simply get the value of title from the database through the data snapshot given back.
I recommend reading the Docs, they're very good and easy to understand when working with different OS/languages.
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
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.
I have two issues with the snippet below.
I have get duplication in my TableView.
When I search for something that returns a result, and I tried searching for another thing that returns a result, the existing result will be the one showing not the new result, for example:
a. I search for "ola" and my TableView returns a list of -> olabode, olatunde, olaide.
b. Then I searched for "bisi", if found matching query for "bisi" I still get the query result for "ola".
func searchView(_ searchView: AZSearchViewController, didTextChangeTo text: String, textLength: Int) {
self.resultArray.removeAll()
searchView.reloadData()
guard text != "" else {
return
}
AppFirRef.userRef.queryOrdered(byChild: "username")
.queryStarting(atValue: text)
.queryEnding(atValue: text + "\\uf8ff")
.observe(.value, with: { (snapshot) in
if (snapshot.value is NSNull) {
print("not found")
} else {
print("found")
self.resultArray.removeAll()
searchView.reloadData()
print("\(snapshot.value)")
for case let snap as FIRDataSnapshot in snapshot.children {
guard let value = snap.value as? [String : Any] else { continue }
//print("\(snap.key)")
print(value)
let user = LContact(value: value, searchUserId: snap.key)
self.set.add(user)
if let username = user.username{
self.resultArray.append(username.lowercased())
searchView.reloadData()
}
}
}
})
}
It might be too late, but change
.observe(.value, with: { (snapshot) in
...
)}
to
.observeSingleEvent(of: .value, with: { (snapshot) in
...
)}
see my answer to this question for the explanation; it applies to you as well because you are also appending to the array every time the data at your reference path changes (such as when someone on another device changes data in the userRef reference path.
I'm working on a simple Firebase project where I am supposed to save an array to Firebase and later retrieve as an array. I managed to save the array to the database but I can't manage to retrieve it and put it into an array.
This is the code I have come the farthest with but I still can't put it into an array
FIRDatabase.database().reference().child("list").observeEventType(.ChildAdded, withBlock: { (snapshot) in
print(snapshot)
}, withCancelBlock: nil)
You can observe the value of a location to get the array.
let ref = FIRDatabase.database().reference().child("list")
ref.observeSingleEventOfType(.Value, withBlock: { snapshot in
if let objects = snapshot.children.allObjects as? [FIRDataSnapshot] {
print(objects)
}
})