My current firebase database structure is like this
customer
-L1x2AKUL_KNTKXyza
name:"abc"
subscription
-L1x2AKlvmG0RXv4gL
sub_no: "123"
sub_name: ""
-L1x2AKlvmG0RXv4ab
sub_no: "456"
sub_name" ""
-L1x2AKUL_KNTKXymk
name:"xyz"
subscription
-L1x2AKlvmG0RXv4xy
sub_no: "789"
sub_name: ""
I am trying to access all subcriptions of all customer records at once.
This is the code I am using:
var ref: DatabaseReference!
ref = Database.database().reference(withPath: "customer")
ref.observe(.value, with: { snapshot in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
let imageSnap = rest.childSnapshot(forPath: "subscription")
let dict = imageSnap.value as! NSDictionary
//self.vehicleListDict.append(dict.object(forKey: "sub_no") as! NSDictionary)
print("value : \(dict)")
}
print("vehicleListDict : \(self.vehicleListDict)")
}) { (error) in
print(error.localizedDescription)
}
I am unable to access all the subscriptions within all customer records at once. Its only accessing till one level. I tried to put a while loop within the while that exists but that also does not give me the output needed. It goes in an infinite loop instead. Please could anyone help. I am using firebase realtime database for the first time.
The fetched values should be
123
456
789
The code for doing specifically what you are asking is
let customerRef = self.ref.child("customer")
customerRef.observe(.childAdded, with: { snapshot in
let subscriptionSnap = snapshot.childSnapshot(forPath: "subscription")
for child in subscriptionSnap.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let subNo = dict["sub_no"] as! String
print(subNo)
}
})
and the output is
a123
a456
a789
*note that I am reading the sub_no as a STRING which is why I added 'a' in front. If they are actually integers change the line to
let subNo = dict["sub_no"] as! Integer
*note2 this will leave a .childAdded observer to the main node in question so any further children that are added will fire the code in the closure.
Edit:
If you want to just retrieve all of the data at one time and not leave a childAdded observer then this will do it:
let customerRef = self.ref.child("customer")
customerRef.observeSingleEvent(of: .value, with: { snapshot in
for customerChild in snapshot.children {
let childSnap = customerChild as! DataSnapshot
let subscriptionSnap = childSnap.childSnapshot(forPath: "subscription")
for subscriptionChild in subscriptionSnap.children {
let snap = subscriptionChild as! DataSnapshot
let dict = snap.value as! [String: Any]
let subNo = dict["sub_no"] as! String
print(subNo)
}
}
})
and the output is
a123
a456
a789
Related
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 need to make multiple observations, but I don't know how.
Here is my database structure:
"Posts" : {
"f934f8j3f8" : {
"data" : "",
"date" : "",
"userid" : ""
}
},
"Users" : {
"BusWttqaf9bWP224EQ6lOEJezLO2" : {
"Country" : "",
"DOB" : "",
"Posts" : {
"f934f8j3f8" : true
},
"Profilepic" : "",
"name" : "",
"phonenumber" : ""
}
I want to observe the posts and I write the code and it works great, but I also want to get the name of the user who posted this post but when I wrote save the name and use it it gives me null. Here is my code.
DataServices.ds.REF_POSTS.queryOrderedByKey().observe(.value,
with: { (snapshot) in
self.posts = []
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
if let postsDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let userID = "BusWttqaf9bWP224EQ6lOEJezLO2"
DataServices.ds.REF_USERS.child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let postusername = value?["name"] as? String ?? ""
})
print(" ------ User name : \(postusername) ------")
})
print(" ------ User name 2 : \(postusername) ------")
let post = Posts(postKey: key, postData: postsDict)
self.posts.append(post)
The first print statement prints the username, but the second one prints nothing.
Thanks in advance.
Firebase is asynchronous so you can't operate on a variable until Firebase populates it within it's closure. Additionally code is faster than the internet so any statements following a closure will occur before the statements within the closure.
The flow would be as follows
Query for the post {
get the user id from the post inside this closure
query for the user info {
create the post inside this second closure
append the data to the array inside this second closure
reload tableview etc inside this second closure
}
}
Something like this edited code
self.posts = []
myPostsRef.queryOrderedByKey().observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
if let postsDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let userID = "BusWttqaf9bWP224EQ6lOEJezLO2"
myUsersRef.child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let userName = value?["name"] as? String ?? ""
let post = Posts(postKey: key, postData: postsDict, name:userName)
self.posts.append(post)
})
}
}
}
})
You're not using the postusername inside the closure so I added that to the Posts initialization.
Also, the self.posts = [] is going to reset the posts array any time there's a change in the posts node - you may want to consider loading the array first, and then watch for adds, changes, or deletes and just update the posts array with single changes instead of reloading the entire array each time.
Edit:
A comment was made about the data not being available outside the loop. Here is a very simplified and tested version. Clicking button one populates the array from Firebase with a series of strings, clicking button 2 prints the array.
var posts = [String]()
func doButton1Action() {
let postsRef = ref.child("posts")
self.posts = []
postsRef.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
let value = snap.value as! String
self.posts.append(value)
}
}
})
}
func doButton2Action() {
print(posts)
}
I am trying to print array from the firebase. Actually if we tap a medication in a list(tableviewcontroller), it will show its specfic dosages. I got stucked to retrieve the dosages list. Here is my code to get data from firebase. Any help is appreciated. Thanks in advance. My firebase structure looks like this.. firebase img
func loadDataFromFirebase() {
databaseRef = FIRDatabase.database().reference().child("medication")
databaseRef.observeEventType(.Value, withBlock: { snapshot in
for item in snapshot.children{
FIRDatabase.database().reference().child("medication").child("options").observeEventType(.Value, withBlock: {snapshot in
print(snapshot.value)
})
}
})
You should take a look on firebase documentation https://firebase.google.com/docs/database/ios/read-and-write
but if I'm understanding your idea, you probably has a model class for your medications. So, to retrieve your data you should do like this for Swift 3.0:
func loadDataFromFirebase() {
databaseRef = FIRDatabase.database().reference().child("medication")
databaseRef.observe(.value, with: { (snapshot) in
for item in snapshot.children{
// here you have the objects that contains your medications
let value = item.value as? NSDictionary
let name = value?["name"] as? String ?? ""
let dossage = value?["dossage"] as? String ?? ""
let type = value?["type"] as? String ?? ""
let options = value?["options"] as? [String] ?? ""
let medication = Medication(name: name, dossage: dossage, type: type, options: options)
// now you populate your medications array
yourArrayOfMedications.append(medication)
}
yourTableView.reloadData()
})
}
Now that you have your array with all your medications, you just need to populate your tableView with this medications. When someone press an item on table you can just call prepareForSegue: and send your yourArrayOfMedications[indexPath.row].options to the next view
The solution is same as above but with a small change.
func loadDataFromFirebase() {
databaseRef = FIRDatabase.database().reference().child("medication")
databaseRef.observe(.value, with: { (snapshot) in
for item in snapshot.children{
// here you have the objects that contains your medications
let value = item.value as? NSDictionary
let name = value?["name"] as? String ?? ""
let dossage = value?["dossage"] as? String ?? ""
let type = value?["type"] as? String ?? ""
let options = value?["options"] as? [String : String] ?? [:]
print(options["first"]) // -> this will print 100 as per your image
// Similarly you can add do whatever you want with this data
let medication = Medication(name: name, dossage: dossage, type: type, options: options)
// now you populate your medications array
yourArrayOfMedications.append(medication)
}
yourTableView.reloadData()
})
}
func generateDataForRecents() {
if URLArrayStringThisSeason.count == 0 {
self.activityIndicator2.isHidden = false
self.activityIndicator2.startAnimating()
}
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("palettes").queryLimited(toFirst: 100).observe(.value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict as [String:AnyObject]{
let URL = each.value["URL"] as! String
self.URLArrayStringRecents.append(URL)
//print(self.URLArrayString.count)
//print(snapshot)
//let pictureTitle = each.value["title"] as! String
print(self.URLArrayStringRecents.count)
}
}
self.whatsNewCollectionView?.reloadData() //Reloads data after the number and all the URLs are fetched
self.activityIndicator2.stopAnimating()
self.activityIndicator2.isHidden = true
})
}
The following code does a retrieval of data each time the function is called, or when a new data is added.
This is extremely useful when the app is first started up or closed and then restarted. However, when the app is running, whenever a new entry is added, the code seemed to run again and thus appending twice the amount of new data.
For example, when there are already 15 entries identified and then suddenly a new entry is added, the array of the URL would contain 15+16 thus amounting to a total of 31.
How do I make it such that the new data is added to the array instead of adding the entire snapshot in?
You do that by listening for .childAdded events, instead of listening for .value:
var query = databaseRef.child("palettes").queryLimited(toFirst: 100)
query.observe(.childAdded, with: { (snapshot) in
let URL = snapshot.childSnapshot(forPath/: "URL").value as! String
self.URLArrayStringRecents.append(URL)
}
Since you have a limit-query, adding a 101st item means that one item will be removed from the view. So you'll want to handle .childRemoved too:
query.observe(.childRemoved, with: { (snapshot) in
// TODO: remove the item from snapshot.key from the araay
})
I recommend that you spend some time in the relevant documentation on handling child events before continuing.
Please check below method. I have use this method not getting any duplicate entry.
func getallNotes()
{
let firebaseNotesString: String = Firebase_notes.URL
let firebaseNotes = FIRDatabase.database().referenceFromURL(firebaseNotesString).child(email)
firebaseNotes.observeEventType(.Value, withBlock: { snapshot in
if snapshot.childSnapshotForPath("Category").hasChildren()
{
let child = snapshot.children
self.arrNotes = NSMutableArray()
self.arrDictKeys = NSMutableArray()
for itemsz in child
{
let childz = itemsz as! FIRDataSnapshot
let AcqChildKey : String = childz.key
if AcqChildKey == AcqIdGlobal
{
if (childz.hasChildren() == true)
{
let dictChild = childz.value as! NSMutableDictionary
self.arrDictKeys = NSMutableArray(array: dictChild.allKeys)
for i in 0..<self.arrDictKeys.count
{
let _key = self.arrDictKeys.objectAtIndex(i).description()
print(_key)
let dictData : NSMutableDictionary = NSMutableDictionary(dictionary: (dictChild.valueForKey(_key)?.mutableCopy())! as! [NSObject : AnyObject])
dictData.setObject(_key, forKey: "notesId")
self.arrNotes.addObject(dictData)
}
}
}
}
self.tableviewNote.reloadData()
}
})
}
As for the query for removed child,
query.observe(.childRemoved, with: { (snapshot) in
print(snapshot)
let URL = snapshot.childSnapshot(forPath: "URL").value as! String
self.URLArrayStringThisSeason = self.URLArrayStringThisSeason.filter() {$0 != URL}
self.thisSeasonCollectionView.reloadData()
})
it will obtain the URL of the removed child and then update the array accordingly.
I have a "flatened" Firebase structure and trying to retrieve a dictionary of values from a "secondary" database member. In other words, I have a conversation which has a "to" cell which has the key to a business listing. With this key, I'm trying to retrieve the business listing and its children (url, description, title). For some reason, I can print the snapshot2.value and it responds with the expected value, but I can't pass it to my class initialization.
DataService.ds.REF_CONVOS.observeEventType(.Value, withBlock: {snapshot in
self.convoListings.removeAll()
self.convoListings = []
//Data parsing from Firebase. The goal is to breakdown the data received and store in a local model.
if let snapshots = snapshot.children.allObjects as? [FDataSnapshot] {
for snap in snapshots {
for convo in userConvos {
// Going into the children of the main object for the conversations.
//print("\(snap)")
if convo == snap.key {
//print(snap.value)
print(snap.value)
if let businessDict = snap.value as? Dictionary<String, AnyObject> {
businessName.removeAll()
let test = businessDict["to"] as? String
DataService.ds.REF_BusinessListing.childByAppendingPath(test).childByAppendingPath("title/").observeSingleEventOfType(.Value, withBlock: { snapshot2 in
print(snapshot2.value)
})
let key = snap.key
let post = ConvoListing(convoKey: key, dictionary: businessDict, businessName: self.test2)
self.convoListings.append(post)
}
}
}
}
}
self.tableView.reloadData()
})
Your nesting seems of:
DataService.ds.REF_BusinessListing.childByAppendingPath(test).childByAppendingPath("title/").observeSingleEventOfType(.Value, withBlock: { snapshot2 in
print(snapshot2.value)
})
let key = snap.key
let post = ConvoListing(convoKey: key, dictionary: businessDict, businessName: self.test2)
self.convoListings.append(post)
Keep in mind the observeSingleEventOfType loads the data asynchronously. For this reason, if you have code that needs the value that you loaded, you need to put that code in the block:
DataService.ds.REF_BusinessListing.childByAppendingPath(test).childByAppendingPath("title/").observeSingleEventOfType(.Value, withBlock: { snapshot2 in
print(snapshot2.value)
let key = snap.key
let post = ConvoListing(convoKey: key, dictionary: businessDict, businessName: self.test2)
self.convoListings.append(post)
})