Read array inside snapshot in Firebase with Swift - ios

Need help trying to read an array of this form:
As far as I tried, I got this
let defaults = UserDefaults.standard
let userUuid = defaults.string(forKey: defaultsKeys.keyOne)
let ref = FIRDatabase.database().reference().child("images").child("\(userUuid!)")
let filterQuery = ref.queryOrdered(byChild: "uuid").queryEqual(toValue: "\(uuid)") // where uuid is a value from another view
filterQuery.observe(.value, with: { (snapshot) in
for images in snapshot.children {
print(images)
}
})
But I receive nothing. I want to read the images' links to show them in the view controller.

Make sure that the uuid var in the line below is not an optional value (or if it is, unwrap it) because otherwise you'll be querying to compare to "Optional(myUuidValue)" instead of "myUuidValue"
let filterQuery = ref.queryOrdered(byChild: "uuid").queryEqual(toValue: "\(uuid)")
The snapshot in the line below contains more than just the images, it has all the other children under that uuid
filterQuery.observe(.value, with: { (snapshot) in })
So extract the images like this:
filterQuery.observe(.value, with: { (snapshot) in
let retrievedDict = snapshot.value as! NSDictionary
let innerDict = retrievedDict["KeyHere"] as! NSDictionary // the key is the second inner child from images (3172FDE4-...)
let imagesOuterArray = userDict["images"] as! NSArray
for i in 0 ..< imagesOuterArray.count {
let innerArray = imagesOuterArray[i] as! NSArray
for image in innerArray {
print(image as! String)
}
}
})
Clarification: cast all the children of the uuid as an NSDictionary, then extract the nested arrays using those two for-loops
Update
Thanks to Jay for pointing out the error! Also, as Jay suggested, consider restructuring your database and replacing those arrays with dictionaries that perhaps contain the URL, path (for deleting purposes if you need that), and timestamp of each image.

After strugling for the answer, got this code works
let ref = FIRDatabase.database().reference().child("images").child("\(userUuid!)")
let filterQuery = ref.queryOrdered(byChild: "identifier").queryEqual(toValue: "\(identifier)")
filterQuery.observe(.value, with: { (snapshot) in
for child in snapshot.children {
if (child as AnyObject).hasChild("images") {
let images = (images as AnyObject).childSnapshot(forPath: "images").value! as! NSArray
for i in images {
for j in i as! [AnyObject] {
let url = NSURL(string: j as! String)
//Then downloaded the images to show on view
URLSession.shared.dataTask(with: url! as URL, completionHandler: { (data, response, error) in
if error != nil {
print(error)
return
}
//Code to show images..
}).resume()
}
}
}
}
})
Can i receive feedback about this?

Related

Retrieve last child from child node firebase realtime database swift 4

This is my firebase realtime database
Image link
This is the snippet i am using its always returning nil
let rootRef = Database.database().reference()
rootRef.child("POSTS").queryLimited(toLast: 1).observeSingleEvent(of: .value) { (myDataSnap) in
let value = myDataSnap.value as? NSDictionary
print(value as? Any)
}
If you are trying to access Id and Id's can be anything other than 0.
Than you can try the snippet below
let Key = rootRef.child("POSTS").childByAutoId().key
rootRef.child("POSTS").child(Key!).setValue(postData.toDictionary()){
(error:Error?, ref:DatabaseReference) in
if let error = error {
print("Data could not be saved: \(error).")
} else {
//do stuff
}
}
Use .childAdded or childChanged instead of .value, this might solve it
let rootRef = Database.database().reference()
rootRef.child("POSTS").queryLimited(toLast: 1).observeSingleEvent(of: .childChanged) { (myDataSnap) in
let value = myDataSnap.value as? NSDictionary
print(value as? Any)
}

How to convert items in optional Dictionary to individual strings

I am trying to convert items in an optional dictionary into individual strings so I can loop through them and convert them into URLs. But have been unable to do so.
Here is function which I use to fetch images from firebase which returns this optional dictionary which is also included below:
func fetchAllUsersImages() {
print("inside func")
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in
print("inside closure")
// print(URL(string: snapshot.value as! String))
// let postSnap = snapshot.childSnapshot(forPath: self.postNum)
// let imageUrlSnap = postSnap.childSnapshot(forPath: "ImageUrl")
print(snapshot.value, "value")
// guard let allImages = imageUrlSnap.children.allObjects as? [DataSnapshot] else { return print("the code failed here")}
guard let allImages = snapshot.value as? [DataSnapshot] else { return print("the code failed here")}
// let snapshotVal = snapshot.value
// let snapshotValValue = snapshotVal as! String
// print(snapshotValValue, "snapshot as string value")
for image in allImages {
print(image, "image")
}
print(snapshot.key, "key")
print(snapshot.value, "value")
print(snapshot.children, "cjildren")
print(allImages)
print()
})
}
Output of snapshot.value:
Optional({
image1 = "https://firebasestorage.googleapis.com/v0/b/base.appspot.com/o/ijzAnEdyKNbhPsQVH6a8mOa1QpN2%2Fpost1%2Fimage1?alt=media&token=c2f396fd-717d-4192-909a-db390dd23143";
image2 = "https://firebasestorage.googleapis.com/v0/b/atabase.appspot.com/o/ijzAnEdyKNbhPsQVH6a8mOa1QpN2%2Fpost1%2Fimage2?alt=media&token=359b8527-f598-4f9a-934e-079cee21fd15";
})
Based on the answer provided I did the followoing:
func fetchAllUsersImages() {
print("inside func")
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in //error here
var images: [URL] = []
if let snapShotValue = snapshot.value as? [String: String] {
for (_, value) in snapShotValue {
if let imageURL = URL(string: value) {
print(imageURL, "image url here")
let imageAsData = try Data(contentsOf: imageURL)
let image = UIImage(data: imageAsData)
let ImageObject = Image()
ImageObject.image = image
self.arrayOfImgObj.append(ImageObject)
self.tableView.reloadData()
}
}
}
})
}
However on the 3rd line I get
Unable to infer closure type in the current context
Edit:
To fix this error put the code, at the deepest part of the code, in a do block amd include a catch block also. This will fix the error.
Well first you need to check if the optional Dictionary exists then loop the dictionary for each key-value pair. Here is a way to do it:
var imageURLs: [URL] = []
if let snapShotValue = snapshot.value as? [String: String] { // Cast optional dictionary to a Dictionary of String keys and String values
// Cast would fail if snapshot.value is nil or has a different Dictionary setup.
for (key, value) in snapShotValue { // you can change key to _ since we are not using it
if let imageURL = URL(string: value) { // Get URL value from string
imageURLs.append(imageURL) // Add new URL to parsed URLs
}
}
}
So once the process is finished you'll have the images in imageURLs variable.

Images fetched from firebase are duplicated in the process of adding them to tableViewCells?

The following code fetches images from firebase, but incorrectly duplicates two images. I think that is due to the placement of the self.tableView.reloadData() None of the placements I've tried work. Can anyone give me suggestions?
func fetchAllUsersImages() {
print("inside func")
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in
if let snapShotValue = snapshot.value as? [String: String] {
for (_, value) in snapShotValue {
if let imageURL = URL(string: value) {
print(imageURL, "image url here")
do {
let imageAsData = try Data(contentsOf: imageURL)
let image = UIImage(data: imageAsData)
let ImageObject = Image()
ImageObject.image = image
self.arrayOfImgObj.append(ImageObject)
} catch {
print("imageURL was not able to be converted into data")
}
}
}
}
})
}
Make sure you clear the array when start calling the function because you are appending data to the array. Secondly, reload table after finished the for loop.
func fetchAllUsersImages() {
self.arrayOfImgObj.removeAll() // clean the array
self.ref.child("Posts").child(self.userID).child(self.postNum).observe(.childAdded, with: { snapshot in
if let snapShotValue = snapshot.value as? [String: String] {
for (_, value) in snapShotValue {
}
tableView.reloadData() // reload view
}
})
}

How to get values inside nested keys in Firebase on Swift

I'm trying to get values inside two nested keys in Firebase.
:
I need to put all the value of name inside an array. Here is my code where I'm accessing just the node "user". I was thinking that I could use "queryOrderedByKey" one after another, but in that case xCode crashes and says something like multiple quires aren't allowed.
Database.database().reference().child("user").queryOrderedByKey().observe(.childAdded) { (snapshot) in
if snapshot.value != nil {
let result = snapshot.value as! [String : AnyObject]
if let name = result["name"] as? String {
self.myArray.append(name)
}
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
}
And this is what I'm getting when printing the result.
Here is the answer
Database.database().reference().child("user").observe(.childAdded) { (snapshot) in
if let dictinoary = snapshot.value as? [String: Any] {
if let myFinalStep = dictinoary["GeneralInformation"] as? [String: Any] {
print(myFinalStep["name"])
}
}
}
Tigran's answer is very good but here's an alternative. This code iterates over each child node within 'user' node and looks into a deep path to get the name. Note this leaves a childAdded observer to the user node so if any additional nodes are added, this will fire again.
let usersRef = self.ref.child("user")
usersRef.observe(.childAdded, with: { snapshot in
if let name = snapshot.childSnapshot(forPath: "GeneralInformation")
.childSnapshot(forPath: "name").value as? String {
print(name)
}
})
If you want to read the names in once and not leave an observer, here's an alternative
let usersRef = self.ref.child("user")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
if let name = snap.childSnapshot(forPath: "GeneralInformation")
.childSnapshot(forPath: "name").value as? String {
print(name)
}
}
})
I was feeling Swifty so here's a third variant if you don't want to leave an observer. Note that if the node containing the name does not exist, the resulting array will contain "No Name" at that index.
let usersRef = self.ref.child("uses")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
let myUsers = snapshot.children.allObjects.map { $0 as! DataSnapshot }
let names = myUsers.map { $0.childSnapshot(forPath: "GeneralInformation")
.childSnapshot(forPath: "name")
.value as? String ?? "No Name" }
print(names)
})

Firebase retrieve autoId value in swift

I would like to retrieve images values. Node look like this so, I would like to retrieve url1, url2, url3.
"Post": {
"uid": {
"Text": "some text",
"images": {
autoID1: url1,
autoID2: url2,
autoID3: url3
}
}
}
The problem is that key is auto generated. So, I usually cast like this.
if let dictionary = snapshot.value as? [String:Anyobject]
let text = dictionary["text"] as? String
but in this case I don't know how to cast autoID key and get value.
I tried code below but error said
Could not cast value of type '__NSDictionaryM' (0x10af491c8) to
'NSString' (0x10a0bad68).
func fetchImages() {
ref.child("Post").child(uid!).child("images").observe(.value, with: { (snapshot) in
let downloadUrl = snapshot.value as! String
let stoRef = Storage.storage().reference(forURL: downloadUrl)
stoRef.getData(maxSize: 1*1024*1024, completion: { (data, error) in
let pic = UIImage(data: data!)
self.imagesArray.append(pic!)
})
}, withCancel: nil)
}
also if there are three url, should I use like for snap in snapshot.children to loop through? How should I loop through those url and put into imagesArray?
Thank you in advance!
You are very close. The thing to remember is that when you get the
.child("images")
it's actually a node that contains children
"images": {
autoID1: url1,
autoID2: url2,
autoID3: url3
and isn't a string but another key: value pair where the key is "images" and the value is the child nodes.
To get the children, iterate over the child nodes in the snapshot, casting each one as a snapshot (another key: value pair) with the key being autoID1 etc and the value is the url (a String)
let imagesRef = self.ref.child("Post").child("uid_0").child("images")
imagesRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
//let key = snap.key //autoID1, autoID2 etc
let downloadUrl = snap.value as! String
let stoRef = Storage.storage().reference(forURL: downloadUrl)
//get your pic
}
})
if ref is a class var, as it appears in your question, it should be referenced as
self.ref
Also, if you want to leave an observer on the node so you can be notified of changes, use .observe. In my case I just wanted to read it one time so I used observeSingleEvent as I only wanted to read it once.

Resources