This is my data in firebase:
override func viewDidLoad() {
database.child("allOrdersTimeline1").observeSingleEvent(of: .value, with: { (snapshot) in
var mixedArray1 = [:] as [String : Double]
let salad = (snapshot.value)
let keyy = snapshot.key
let valuee = (snapshot.children.allObjects) //as [String : Double])
self.labelWhatLabel.text = ("\(valuee)")
print("There are \(snapshot.childrenCount) children found")
print("1")
print(keyy)
print("2")
print(valuee)
print("3")
print(salad)
print("4")
print("Ny test: \(mixedArray1)")
})
No matter what I try, I keep getting the full line (name and time) as such:
I can't figure out how to only get the hour/Double with out the name.
(by the way, im very new and this might be very dumb - My sincere apologies.
I think you might be looking for:
print(snapshot.childSnapshot(forPath:"Agerskov").value)
If you don't know the keys of the child nodes, you can loop over snapshot.children to access the child snapshots:
for child in snapshot.children {
let snap = child as! DataSnapshot
print(snap.key)
print(snap.value)
}
Also see these results when you search for [firebase-realtime-database][swift] loop over children.
Related
How can i read all child data from Firebase.
let ShopRef = Database.database().reference(withPath: “ShoppingMallLst”).child(“ShoppingMall1”)
ShopRef.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
for data in snapshot.children.allObjects as! [DataSnapshot] {
if let data = data.value as? [String: Any] {
let Description = data["Description"] as? String
let Floor = data[“Floor”] as? Int
….
}
}
}
})
But how can i read the data from child "ShopPath?"
child "ShopPath" has type [String: [String: String]]
you can try:
let ShopPath = data["ShopPath"] as? [String: [String: String]]
The key to firebase is to keep data in snapshots as long as you can. So in this case instead of casting items to dictionaries, which loose ordering or arrays that get more complex as the structure gets deeper, leverage DataSnapshots.
Here's the main function to read all shops in ShoppingMall1 - similar to the code in your question.
func readMallShops() {
let ref = self.ref.child("ShoppingMallList").child("ShoppingMall1")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allShopsSnap = snapshot.children.allObjects as! [DataSnapshot]
for shopSnap in allShopsSnap {
let shop = ShopClass(withSnap: shopSnap)
}
})
}
Then a class that holds data about each shop. Note that I pass each snapshot in to initialize the class
class ShopClass {
var name = ""
var height = ""
convenience init(withSnap: DataSnapshot) {
self.init()
let name = withSnap.childSnapshot(forPath: "ShopName").value as? String ?? "No Shop Name"
print("Shop: \(name)")
self.name = name
let shopPathSnap = withSnap.childSnapshot(forPath: "ShopPath")
let shopChildSnap = shopPathSnap.children.allObjects as! [DataSnapshot]
for childDataSnap in shopChildSnap { //iterate over the array in ShopPath
let height = childDataSnap.childSnapshot(forPath: "Height").value as! String
print(" height: \(height)")
self.height = height
}
}
}
And the output looks like this
Shop name: Test
height: 1,180
Shop name: Test 2
height: 2,000
I left off the other child nodes as if you can read height, you can read the rest. So this just assigns and prints out the shop name and height (as a string).
A suggestion as well. Arrays are not well suited for NoSql databases and their use is very situational (avoid if possible). If you're using an array, there's probably a better structure available.
Basically everything is working, except the showChild func is returning completion([]) because of the guard catData = Category(snapshot: catInfo). I am wondering why the guard let is returning completion. When I debug, catInfo does have 1 value as shown in my pic of database and I want to append catData.main to "cats". Below is code for the service method and Category model as well.
Firebase Database
static func showChild(completion: #escaping ([String]) -> Void) {
let ref = Database.database().reference().child("category").child(User.current.uid)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let snapshot = snapshot.children.allObjects as? [DataSnapshot] else {
return completion([])
}
var cats = [String]()
for catInfo in snapshot {
guard let catData = Category(snapshot: catInfo) else {
return completion([])
}
cats += catData.main
}
completion(cats)
})
}
import Foundation
import FirebaseDatabase.FIRDataSnapshot
class Category {
var key: String?
let main: [String]
init?(snapshot: DataSnapshot) {
guard !snapshot.key.isEmpty else {return nil}
if let dict = snapshot.value as? [String : Any]{
let main = dict["main"] as? [String]
self.key = snapshot.key
self.main = main ?? [""]
}
else{
return nil
}
}
}
The issue is pretty straightforward.
While your snapshot contains at least one node of data, it's not in a format that the Category init method understands. You're iterating over it's child nodes and in your screenshot, there's only one, with a key of 'main'
You are observing this node
fb_root
category
2ayHe...
and then you're iterating over it's child nodes which will be
main
0: Performance
so the key is 'main' and it's value is '0: Performance'
but your Category class is looking for a child node of 'main'
let main = dict["main"] as? [String]
There's not enough info to understand what will be contained in the rest of the structure so I can't tell you how to correct it, but at least you know what the problem is.
To clarify, this line
if let dict = snapshot.value as? [String : Any]
will make dict = [0: "Performance]
I have this database (see the link above with the image) and a code with a path, that leads to the destination using:
databaseReference.child("users").child(currentUser!.uid).child("todo-list")
Under the first node "users", the next level is a keyvalue for userID, and later there are more sub-levels "todo-list" etc.
I would like to fetch the data from all uid available. How can I make the query?
Here is the code:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
databaseReference = Database.database().reference()
currentUser = Auth.auth().currentUser
let todoListRef = databaseReference.child("users").child(currentUser!.uid).child("todo-list")
todoListRef.observe(DataEventType.value, with: { (DataSnapshot) in
self.itemsToLoad.removeAll()
let enumerator = DataSnapshot.children
while let todoItem = enumerator.nextObject() as? DataSnapshot
{
let item = todoItem.value as AnyObject
self.itemsToLoad.append(item)
}
self.itemsToLoad = self.itemsToLoad.reversed()
self.tableView.reloadData()
})
}
It's quite similar to the code you already have. Instead of observing /users/$uid, you observe the entire /users node. And then you add an extra loop in the closure, to iterate over all the users.
So:
let usersRef = databaseReference.child("users")
usersRef.observe(DataEventType.value, with: { (usersSnapshot) in
let userEnumerator = usersSnapshot.children
while let user = userEnumerator.nextObject() as? DataSnapshot {
let uid = user.key
let todoEnumerator = user.childSnapshot(forPath: "todo-list").children
while let todoItem = todoEnumerator.nextObject() as? DataSnapshot {
let item = todoItem.value as AnyObject
self.itemsToLoad.append(item)
}
}
})
The added while loops over the users, while the inner while loops over the todo's for each user. I removed the code related to the table view, since what you want to do there doesn't depend on Firebase.
Im successfully getting data from Firebase but I can't manage to push it into array to use. My database is as follows:
users
-Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
-email : "mike#gmail.com"
-lists
-LJiezOzfDrqmd-hnoH-
-owner: Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
-LJif-UgPgbdGSHYgjY6
-owner: Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
shopping-lists
-LJh6sdBJtBCM7DwxPRy
-name: "weekly shopping"
-owner: "mike#gmail.com"
I have a home page after login that shows existing shopping lists on table if they exist. On viewDidLoad() I get shopping list IDs from the user and use those IDs as a reference to get details from shopping-lists.
However, I cant manage to save these data into an array as it gets deleted after closure. How can I do that in a clean way?
override func viewDidLoad() {
super.viewDidLoad()
SVProgressHUD.show()
tableView.allowsMultipleSelectionDuringEditing = false
// Sets user variable - must have
Auth.auth().addStateDidChangeListener { auth, user in
guard let user = user else { return }
self.user = User(authData: user)
// If new user, write into Firebase
self.usersRef.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.hasChild(self.user.uid) {
self.usersRef.child(user.uid).setValue(["email": user.email!])
}
})
// Get shopping lists data from "users/lists"
self.usersRef.child(user.uid).child("lists").observe(.value, with: { snapshot in
// Get list IDs
if snapshot.exists() {
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
self.listNames.append(child.key)
}
}
}
// Use list IDs - to get details
for item in self.listNames {
let itemRef = self.shoppingListsRef.child(item)
itemRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
let name = value["name"] as? String ?? ""
let owner = value["owner"] as? String ?? ""
let shoppingList = ShoppingList(name: name, owner: owner)
self.items.append(shoppingList)
}
})
}
})
self.tableView.reloadData()
SVProgressHUD.dismiss()
}
}
(the question is a bit unclear so several parts to this answer to cover all possibilities. This is Swift 4, Firebase 4/5)
You don't really need to query here since you know which nodes you want by their key and they will always be read in the in order of your listNames array. This assumes self.listNames are the keys you want to read in.
for item in listNames {
let itemRef = shoppingListsRef.child(item)
itemRef.observe(.value, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
let name = value["name"] as? String ?? ""
let owner = value["owner"] as? String ?? ""
print(name, owner)
}
})
}
Generally, queries are used when you are searching for something within a node - for example if you were looking for the node that contained a child name of 'weekly shopping'. Other than that, stick with just reading the nodes directly as it's faster and has less overhead. Keep reading...
I also removed the older NSDictionary and went with the Swift [String: Any] and modified your error checking
However, the real issue is reading that node with an .observe by .value. Remember that .value reads in all children of the node and then the children need to be iterated over to get each separate DataSnapshot. Also, .observe leaves an observer on the node notifying the app of changes, which I don't think you want. So this will answer the question as posted, (and needs better error checking)
for item in listNames {
let queryRef = shoppingListsRef
.queryOrdered(byChild: "name")
.queryEqual(toValue: item)
queryRef.observe(.value, with: { (snapshot) in
for child in snapshot.children { //even though there is only 1 child
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let name = dict["name"] as? String ?? ""
let owner = dict["owner"] as? String ?? ""
print(name, owner)
}
})
}
And the answer...
This is probably more what you want...
for item in listNames {
let queryRef = shoppingListsRef
.queryOrdered(byChild: "name")
.queryEqual(toValue: item)
queryRef.observeSingleEvent(of: .childAdded, with: { snapshot in
let dict = snapshot.value as! [String: Any]
let name = dict["name"] as? String ?? ""
let owner = dict["owner"] as? String ?? ""
print(name, owner)
})
}
note the .childAdded instead of .value which presents the snapshot as a single DataSnapshot and doesn't need to be iterated over and the .observeSingleEvent which does not leave an observer attached to each node.
Edit
Based on additonal information, it would be best too change the structure to this
shopping-lists
-LJh6sdBJtBCM7DwxPRy
-name: "weekly shopping"
-uid: "Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2"
and then when the user logs in just query the shopping lists node for any uid that's theirs.
i am attempting to retrieve a list of Posts ("Planits - in my apps language") from firebase. My goal is to display a specific users posts within a table view on their profile. I have written a function to retrieves posts and query them by a sender ID so that the user see's their posts on their profile. But at the end of the query when i try to print out the appended array, i keep getting an empty array, so i can not go further on to populate the table view. Please any suggestions on where i went wrong, attached is a screen shot of my firebase nodes and the function i wrote. thanks
func retrievePost(){
ref = Database.database().reference()
let myPlanitsRef = self.ref.child("planits")
let query = myPlanitsRef.queryOrdered(byChild: "senderId").queryEqual(toValue: "uid")
print(query)
query.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
for child in snapshot.children {
let snap = child as! DataSnapshot
print(DataSnapshot.self)
let dict = snap.value as! [String: Any]
let myPostURL = dict["images"] as! String
self.images.append(myPostURL)
}
//print(myPostURL) - DOES NOT PRING ANYTHING
//print(self.images) - DOES NOT PRING ANYTHING
}
}) { (error) in
print(error)
}
}
override func viewDidLoad() {
super.viewDidLoad()
retrievePost()
print(images) // PRINTS []