How to loop through Firebase data - ios

How to loop through Firebase data (childs) which are actually objects and access to their properties in Swift 4?
As a beginner with Swift, I am trying to loop through data I am retrieving from Firebase, and I am trying to access to properties of those objects. Seems much more complicated then it should be in swift (just my subjective opinion)
As per documentation on the Firebase site this is what I have
_commentsRef.observe(.value) { snapshot in
for child in snapshot.children {
// Access to childs here ...
}
}
Now, combining this above and as per tutorials and explanations (btw was not able to find neither one which explains this fully) which I found on net, this is what I have:
ref.child("activities").child("list").observeSingleEvent(of: .value, with: { (snapshot) in
// The list i got here is the list of the childs which are objects
// Lets loop through that list and pull properties we need
for child in snapshot.children.allObjects as! [DataSnapshot] {
print(child.value)
}
})
The print in the loop will properly display object with all of its properties, but I am not able to access to these properties. Accessing to it with something like "child.value.title" is resulting with error "Value of type 'Any' has no member 'title'"
Do I need to convert child.value to something else, maybe to cast it or to convert it somehow to property accessible JSON or something like that?

If you call value on a snapshot that contains multiple properties, what you get back is a NSDictionary with the property names as the keys. So to get the value of the title key you'd do:
for child in snapshot.children.allObjects as! [DataSnapshot] {
print(child.value)
let dict = child.value as? [String : AnyObject] ?? [:]
print(dict["title"])
}
Alternatively you can use the other members of DataSnapshot to navigate to the title property and then call .value on that:
for child in snapshot.children.allObjects as! [DataSnapshot] {
print(child.value)
print(child.childSnapshot(forPath: "title").value)
}
See DataSnapshot.value and the first sample in the Firebase documentation on reading data.

Related

After reading data from Firestore cells in tableView randomly reorder

I'm working on application that prints name of place that has been visited according to its ID. I'm storing data in several nodes node called "placesExploredByUsers/userID" stores data about IDs of places that user have visited before and node "databaseOfPlaces" stores all IDs of places with additional info (name, location, coordinates etc.), so it works like foreign key in SQL. All of my data is stored in Firebase.
But, I'm having problem with ordering my cells in tableView in Swift. I've tried several options and this is my last modification. The problem is, that my cells are randomly reordering everytime I come to the viewController with tableView.
This is function, that should handle it.
Can anyone please help me? Thanks in advance
func getDataToTable(){
// setting the firebase reference
ref = Database.database().reference()
let userID = (Auth.auth().currentUser?.uid)!
// getting information about places that user has visited
Database.database().reference().child("placesExploredByUsers").child(userID).queryOrderedByValue().observeSingleEvent(of: .value) { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
// creating array of all ID of places that user has visited
let idsOfAllPlaces = Array(dictionary.keys)
for id in idsOfAllPlaces {
// getting information about places from database of ID of all places that are in app
Database.database().reference().child("databaseOfPlaces").child(id).queryOrderedByValue().observeSingleEvent(of: .value) { (snapshot1) in
if let dictionary = snapshot1.value as? [String: AnyObject]{
// getting information about name
self.random = dictionary["name"]! as! String
// updating the table view
self.postData.append(self.random)
self.sortedList(array: self.postData)
}
}
}
}
}
}
as per documentation
By default, a query retrieves all documents that satisfy the query in
ascending order by document ID. You can specify the sort order for
your data using orderBy(), and you can limit the number of documents
retrieved using limit().
Note: An orderBy() clause also filters for existence of the given field. The result set will not include documents that do not contain the given field.
So use order(by: "databaseOfPlaces")
func getDataToTable(){
// setting the firebase reference
ref = Database.database().reference()
let userID = (Auth.auth().currentUser?.uid)!
// getting information about places that user has visited
Database.database().reference().child("placesExploredByUsers").child(userID).order(by: "databaseOfPlaces").observeSingleEvent(of: .value) { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject]{
// creating array of all ID of places that user has visited
let idsOfAllPlaces = Array(dictionary.keys)
for id in idsOfAllPlaces {
// getting information about places from database of ID of all places that are in app
Database.database().reference().child("databaseOfPlaces").child(id).queryOrderedByValue().observeSingleEvent(of: .value) { (snapshot1) in
if let dictionary = snapshot1.value as? [String: AnyObject]{
// getting information about name
self.random = dictionary["name"]! as! String
// updating the table view
self.postData.append(self.random)
self.sortedList(array: self.postData)
}
}
}
}
}
}
The problem is that you call snapshot1.value as? [String: AnyObject], which means the order is lost (because items in a dictionary have no defined order).
To process the results in the order you requested them, loop over snapshot1.children. Something like:
for snap in snap1.children.allObjects as! [DataSnapshot] {
self.random = snap.value["name"]! as! String
self.postData.append(self.random)
self.sortedList(array: self.postData)
}
Also see:
Firebase getting data in order
iOS - Firebase Filter query
How to properly use queryOrderedByValue

How can I convert a Firebase Database Snapshot into an Array List in Swift?

I am making an app using Swift and Firebase. I want to get an array list containing all of the numbers under a certain user id (images attached below). What I mean by this is I want to be able to call a function that returns an array list containing every integer (from low to high) placed as a child of the user id, but not containing their values (in this case "true"). I have already gotten a snapshot of the data (see code below), but I am unsure as to what to do now.
My Code:
func likeToLikeForAll() {
let uid = Auth.auth().currentUser?.uid
Database.database().reference().child("Liked Movies").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
print()
print("SNAP:")
print(snapshot.value!)
print()
}
})
}
The function prints:
This is an image of the realtime database:
Create an array from the keys
let array = Array(dictionary.keys)

Firebase: what is a FirDatabaseQuery object actually used for?

I've found millions of examples and tutorials on how to obtain a FirDatabaseQuery object (or DatabaseQuery as its now been renamed to) using one of the query....() methods.
But not one of these examples then goes on to show what you can do with the query once you have obtained it.
If you use one of the query methods to obtain a subset of the data as a DatabaseQuery, how do you actually access the set of data objects that it represents?
If you can't, for example, iterate through the query results like you can iterate through a snapshot, then what is a DatabaseQuery actually used for then?
I think that the answer to this is easily found in the docs.
I think that in the Android version of the class it is explained pretty well:
The Query class (and its subclass, DatabaseReference) are used for reading data. Listeners are attached, and they will be triggered when the corresponding data changes.
I can explain it in my own words and more practical like this:
You can use the DatabaseQuery object like a DatabaseReference just with the limitation that only the observe functions work and none of the methods used to manipulate the value, i.e. setValue() etc., of the reference nor any methods related to other nodes, i.e. parent and child (child() etc.), exist.
(I took the code from the docs, so please be free to optimize it/bring in your coding style)
This is how you get data from a DatabaseReference:
var postRef: DatabaseReference!
postRef = Database.database().reference().child("posts")
refHandle = postRef.observe(DataEventType.value, with: { (snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
// ...
})
And this is how you get data from a DatabaseQuery:
// My top posts by number of stars
let myTopPostsQuery = (ref.child("user-
posts").child(getUid())).queryOrdered(byChild: "starCount")
refHandle = myTopPostsQuery.observe(DataEventType.value, with: { (snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
// ...
})
As you can see the second part remains the same, as I mentioned, so in retrieving data you handle a DatabaseQuery like a DatabaseQuery. But I have to agree with you on the point that the iOS especially Swift docs are not that well made, the explanations for the other languages are way clearer.

Storing children of Firebase Database in an array

I have set up a Firebase database that has a parent and many different children. I am attempting to save all of the children's keys into an array that I can access elsewhere in the code.
// Used to get the children
rootRef.child(partyName).observe(.childAdded, with: { (snapshot) in
var newItems = [FIRDataSnapshot]()
for item in snapshot.children {
newItems.append(item as! FIRDataSnapshot)
}
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict{
let keyID = each.key
saves.append(keyID)
}
}
})
Unfortunately, most of the "solutions" that I have found online simply print the retrieved data or add it to a Table View. I simply want all the children to be saved in an array that I can access later. Is this possible? If so, how would I do it?
Thanks
The proper way of storing the retrieved data is to store the key-value pairs in an NSDictionary or a Swift Dictionary. As Sachin Vas said, it makes no sense to store just the keys because then you'd have no relation back to the values.
Nevertheless, to answer your question, the code you provided in your question does what you're asking. If saves is a global or static array in some class, it would be accessible globally throughout your application and would contain all the retrieved keys.
The right way of storing the retrieved data would work just the same way, except saves would be a Dictionary.
rootRef.child(partyName).observe(.childAdded, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject] {
saves = snapDict // where saves is declared as Dictionary<String, AnyObject>
}
})

Firebase sorting clarification on snapshot.value

Was hoping someone could help me understand Firebase snapshot and sorting. Why if you queryOrderedByChild on a FirebaseDBRef, the snapshot.value returns as displayed on the Firebase Database (using the browser), but if you do for snap in snapshot.children{} the values do show sorted. Just wanting clarification on why this happens.
Let me explain with example.
let dinoRef = rootRef.child("dino")
let query = dinoRef.queryOrderedByChild("dimensions/height")
query.observeSingleEventOfType(.Value, withBlock: { snapshot in
// This prints to the console unsorted
print(snapshot.value)
// This prints to console sorted
for snap in snapshot.children {
print(snap)
}
})
Thank you.
A FIRDataSnapshot contains information about the keys of the returns data, the values associated with those keys and the order of the keys. When you call snapshot.children the snapshot the enumerator will return the children in the order you requested.
When you call snapshot.value on a snapshot, it must convert all the information to an NSDictionary. A dictionary has no inherent information on the order of data, so the order of the results is lost at this point.
You must try order by child with ascending and descending option. That will clarify your doubt.

Resources