How do I get specific values from children in firebase using Swift 4? - swift

My Firebase Database
More specifically, I have randomly generated children(Listings) and from those randomly generated Listings I would like to get the string value from the keys.
For example, if I wanted the Photo URL address, I would like to get the string value of the key "PhotoURL:".
Thank you in advance !

First you need to do is to import Firebase and then call a function from the Database class like so:
let ref = Database.database().reference().child("Listings")
You can call child recursively to go deeper into your tree
//.child("Listings").child("SomeListing").child("PhotoURL")
Then call observeSingleEvent to receive the values from firebase.
Your value is stored in the snapshot variable
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let listingsDictionary = snapshot.value as? [String: Any] else { return }
listngsDictionary.forEach({ (key, value) in
// Here you can iterate through it
})
}) { (err) in
print("Failed to fetch following listings:", err)
}

Here is the code to get child values from Listings. 
var ListArr = [ListModel]()
let ref = Database.database().reference().child("Listings")
ref.observe(.childAdded, with: { (snapshot) in
print(snapshot)
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
let Obj = ListModel()
Obj.UID = snapshot.key
Obj.PhotoURL = dictionary["PhotoURL"] as? String
self.ListArr.append(Obj)
}, withCancel: nil)
}
You can set up the model class
class ListModel: NSObject {
var UID:String?
var PhotoURL:String?
}

Related

How can i fetch specific data from firebase and store it in a dictionary?

I'm trying to fetch data from my firebase database, such that i can store it in a form of dictionary which is a type [String: [Any]] where key is the unique id and the value is a type of array which has the data stored under uniqueID->Question.
func getData(currUser: String, completion: #escaping (([String : [Any]]) -> ())) {
var newArray = [String : [Any]]()
let ref = Database.database().reference(fromURL: "MYURL").child("users/\(currUser)/Questions").observeSingleEvent(of: .value, with: { (snap) in
let enumerator = snap.children
while let rest = enumerator.nextObject() as? DataSnapshot, let value = rest.value{
newArray.updateValue([value], forKey: rest.key)
}
completion(newArray)
})
}
this completion block gives me:
["-LlpbizBpQTXOQ6zv0zd": [{
Qusetion = (
Hello,
test,
kkkkkkkkkkk
);
}]]]
Instead how can i get
["-LlpbizBpQTXOQ6zv0zd": [Hello,test,kkkkkkkkkkk]]
You're converting the value to a string, while it's actually a JSON object. That's why the value in your dictionary is a JSON object.
To only get the question text under Qusetion (typo?), you'll need to loop over that child snapshot and collect the individual values. Something like:
var newArray = [String : [Any]]()
let ref = Database.database().reference(fromURL: "MYURL").child("users/\(currUser)/Questions").observeSingleEvent(of: .value, with: { (snap) in
let enumerator = snap.children
while let rest = enumerator.nextObject() as? DataSnapshot {
var values = [String]
let valueEnumerator = rest.childSnapshot(atPath: "Qusetion").children
while let valueRest = valueEnumerator.nextObject() as? DataSnapshot, let value = rest.value {
values.append(value)
}
newArray.updateValue([values], forKey: rest.key)
}
completion(newArray)
})

Firebase Snapshot Isn't showing Data

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]

Swift 4, Firebase how to observe the snapshot and convert into object

My Firebase datastructure looks like this:
user is the parent, underneath it is the user uid and Dream, Grocery list is the note titles and they have their content. I am trying to observe the value of the titles and put them into an array to put in a tableview. This is what I have so far but since the titles of the note are different every time, I dont know how to do a model for that:
DataService.ds.REF_CURRENT_USER.observe(.value) { (snapshot) in
for snap in snapshot.children.allObjects as! [DataSnapshot] {
print("Note Title:\(snap.key)")
}
}
This prints out everything including email and provider but I dont want that I just want it to print Dream and Grocery List
Use the below code to get your required values
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let ref = Database.database().reference().child("users").child("\(uid)")
ref.child("Dream").observe(.value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
print(dictionary["content"] as? String)
}, withCancel: nil)
ref.child("Grocerylist").observe(.value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
print(dictionary["content"] as? String)
}, withCancel: nil)

swift firebase nested children dictionary delete

firebase structure
In the firebase structure you can see i have to delete specific user (currentUserId) in all the groups:
it's what i try to do:
###########################UPDATED###################################
let groupsRef = self.root.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
var dict = groupSnap.value as! [String: Any]
let uid = dict["utenti"] as! [String: Bool]
for each in uid {
if each.key == self.currentUserID{
print(each.key)
//i now need a way to remove this key:value
}
}
}
})
I'm new so i'm not able to go further in extracting every key of ditcionary, than i will compare to the one i have to delete and if it's the same i will delete.
Can someone help?
let groupsRef = self.root.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
let groupKey = groupSnap.key
//added a groupKey to track the id of each group
var dict = groupSnap.value as! [String: Any]
var uid = dict["utenti"] as! [String: Bool]
//then for each key:value in uid check if is there a key = to currentuserID
for each in uid {
if each.key == self.currentUserID{
uid.removeValue(forKey: each.key)
//here you remove currentuserId from the dictionary and below
//finally you set back the new value of the dictionary without currentuserId
self.root.child("groups").child(groupKey).child("utenti").setValue(uid)
}
}
}
})
you can use
for (key, value) in uid {
}
to loop over a dictionary.
But really, looking in the official documentation of swift would give you the right answer...
let groupsRef = self.root.child("groups")
groupsRef.observeSingleEvent(of: .value, with: { snapshot in
for groupChild in snapshot.children {
let groupSnap = groupChild as! DataSnapshot
for subGroupChild in groupSnap.children {
//Here you need to remove that specific user if condition match
//specificUserUID is that user's id that user to be deleted
if let snapref = subGroupSnap as! DatabaseReference {
snapref.queryOrdered(byChild: "utenti").queryEqual(toValue: specificUserUID).observe(.childAdded, with: { (snapshot) in
snapshot.removeValue(completionBlock: { (error, reference) in
if error != nil {
print("There has been an error:\(error)")
}
})
})
}
}
}
})
Your code will go like above you just need find thaat user by user ID and delete that particular snapshot.

Firebase one of two observers not working

I have two observers, the second observer is dependent on the first observers value. I can't seem to get the first observer to work, I am not getting any errors on Xcode. The first function has to check the Users profile for information and then use that information to search for different information in the database. Here is my code:
func loadposts() {
ref = Database.database().reference()
let trace = Performance.startTrace(name: "test trace")
trace?.incrementCounter(named:"retry")
let userID = Auth.auth().currentUser?.uid
print(userID!)
ref.child("Users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let one1 = value?["Coupon Book"] as? String ?? ""
print("one1: \(one1)")
self.bogus.set(one1, forKey: "bogus")
}) { (error) in
print(error.localizedDescription)
}
delay(0.1) {
print("bogus: \(self.bogus.string(forKey: "bogus"))")
Database.database().reference().child("Coupons").child(self.bogus.string(forKey: "bogus")!).observe(.childAdded) { (Snapshot : DataSnapshot) in
if let dict = Snapshot.value as? [String: Any] {
let captiontext = dict["company name"] as! String
let offerx = dict["offer count"] as! String
let logocomp = dict["logo"] as! String
let actchild = dict["childx"] as! String
let post = Post(captiontext: captiontext, PhotUrlString: actchild, offertext: offerx, actualphoto: logocomp)
self.posts.append(post)
self.tableview.reloadData()
print(self.posts)
}
}
}
trace?.stop()
}
Any help is appreciated.
self.bogus.string(forKey: "bogus"))" is nil because observeSingleEvent is an async method, so to get the required results you need to call the second observer inside the first observer or you can use the completion handler
You can use the completionHandler like this:
guard let uid = Auth.auth().currentUser?.uid else {
return
}
func firstObserverMethod(completionCallback: #escaping () -> Void) {
ref.child("Users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
if let value = snapshot.value as? [String: Any] {
let one1 = value["Coupon Book"] as? String
print("one1: \(one1)")
self.bogus.set(one1, forKey: "bogus")
completionCallback()
}
}) { (error) in
print(error.localizedDescription)
}
}
Now using the above method:
firstObserverMethod {
print("bogus: \(self.bogus.string(forKey: "bogus"))")
guard let bogusString = self.bogus.string(forKey: "bogus") else {
print("bogus is not set properly")
return
}
Database.database().reference().child("Coupons").child(bogusString).observe(.childAdded) { (Snapshot : DataSnapshot) in
if let dict = Snapshot.value as? [String: Any] {
let captiontext = dict["company name"] ?? ""
let offerx = dict["offer count"] ?? ""
let logocomp = dict["logo"] ?? ""
let actchild = dict["childx"] ?? ""
let post = Post(captiontext: captiontext, PhotUrlString: actchild, offertext: offerx, actualphoto: logocomp)
self.posts.append(post)
DispatchQueue.main.async {
self.tableview.reloadData()
}
print(self.posts)
}
}
}
Note: You should use optional binding to get the values from optional
Since you are using the result of the 1st observer in the reference of your 2nd observer, it's a very bad idea to add the 2nd observer right below the first observer. And adding a delay won't be a viable solution : these two calls are asynchronous, which means that the reason why you are not getting might very likely be because the 2nd observer is triggered even before the 1st has returned any data.
The solution here, would be using a completion handler, or you could just incorporate your 2nd observer inside the completion block of the 1st, to be make sure that the proper order (1st observer -> 2nd observer) will always be respected.
It would look somehow like this:
func loadposts() {
// ...
// 1st Observer here
ref.child("Users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get your value here
guard let one1 = snapshot.childSnapshot(forPath: "Coupon Book").value as? String else { return }
// 2nd Observer here. Now you can use one1 safely:
Database.database().reference().child("Coupons").child(one1).observe(.childAdded) { (Snapshot : DataSnapshot) in
// ...
}
})
}
Now, a couple of things that you could also improve in your code, while not directly related to the question:
I would suggest you to make use of guard statements instead force-unwrapping, which may end up in crashing your app at some point.
For example, you could check whether your current user exist or not like so:
guard let currentUserID = Auth.auth().currentUser?.uid else {
return
}
// Now you can use safely currentUserID
Also, when you try to get the data out of the snapshot, it's not a good idea either, to use force-casting. You would better write it in this way:
yourRef.observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
guard let text = child.childSnapshot(forPath: "text").value as? String, let somethingElse = child.childSnapshot(forPath: "otherValue").value as? NSNumber else {
return
}
// And so on, depending of course on what you have in your database.
}