iOS - Firebase Filter query - ios

I got score node in Firebase:
And under score node my data structure is as:
{
"jjTO29ziQ2SjrH5PFzY7ZMpSgjq1-Kak6FCyGtdy_kcEPd4K" = {
jjTO29ziQ2SjrH5PFzY7ZMpSgjq1 = "-Kak6FCyGtdy_kcEPd4K";
score = 5;
};
"jjTO29ziQ2SjrH5PFzY7ZMpSgjq1-KbE_pgfUsukOm4uW0bx" = {
jjTO29ziQ2SjrH5PFzY7ZMpSgjq1 = "-KbE_pgfUsukOm4uW0bx";
score = 4;
};
Question:
Can I filter the data by score ?
I tried doing :
FIRDatabase.database().reference().child("scores").queryOrdered(byChild: "score").observeSingleEvent(of: .value, with: { (snapshot) in
debugPrint(snapshot.value ?? "nil")
})
But can't get the result ?

When you execute a query against Firebase, you get a FIRDataSnapshot that contains three pieces of information for each result:
its key
its value
its position relative to other nodes
When you're calling snapshot.value the snapshot is converted into a Dictionary. Unfortunately that dictionary can only hold key-value pairs, so you lose the information on the position of the nodes. So while the data in the snapshot is actually sorted correctly, you're throwing that information away.
The solution is to use the FIRDataSnapshots built-in children property to loop over the child nodes in the correct order. The Firebase documentation on querying has this sample of how to do that:
_commentsRef.observeEventType(.Value, withBlock: { snapshot in
for child in snapshot.children {
...
}
})

Related

Firebase Database Read childByAutoId

Im currently learning ropes in Firebase for iOS, so bare with my novice skills.
Below is a screenshot of my database:
gZ7dn2LMkiah is the autoId for a user
LDBTRFVS8dtz and LDBTe4oXs are autoIds for childId for this user
How can I read the two child nodes inside the node gZ7dn2LMkiah? Cause from my code below, it can only be possible if I have only one child underneath this node, not two
ref = Database.database().reference()
let userId: String = (Auth.auth().currentUser?.uid)!
databaseHandle = ref?.child("childId").child(userId).observe(.childAdded, with: { (snapshot) in
I tried adding childByAutoId after child(userId) but it didn't return any results.
Any help would be appreciated. Thanks in advance.
Database.database().reference(withPath:
"childId").child(userId).observe(.childAdded)
{ (snapshot:DataSnapshot) in
// This will print all sub node
print(snapshot)
}
First of all your db seems incorrect. In first node nick and relation are inside the autoGeneratedKey and in second node nick and relation are outside the key as both are visible while the node is collapse. So these values should be inside autoGeneratedKey. Please change your db structure in correct way. See below screenshot:
This will be your childs table containing all childs for all parents and you can query for a particular parent to get his childs. See below code snippet:
ref.child("childs").queryOrdered(byChild: "parentId").queryEqual(toValue: "123").observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.exists() {
print("exists")
for child in snapshot.children {
let data = child as! DataSnapshot
print(data.key)
print(data.value)
}
}
else {
print("doesn't exist")
}
}
Output:
-LDBus9Xas3oTccwPN4r
Optional({
nick = Dave;
parentId = 123;
relation = Son;
})
-LDBus9_Uz_qe69e9CXK
Optional({
nick = Susan;
parentId = 123;
relation = Daughter;
})
Where parentId is let userId: String = (Auth.auth().currentUser?.uid)!.
Note 1: I tried adding childByAutoId after child(userId) but it didn't return any results.
Yes it will not work because childByAutoId generate a new key which will never match with existing db keys so you will get nothing.
Note 2: When to use .childAdded
.childAdded event is to listen new entry for the node reference not to fetch the data. To fetch the data for once we should use .observeSingleEvent event.
Note 3:Cause from my code below, it can only be possible if I have only one child underneath this node, not two
No its not possible. This is just because of second node's nick and relation are outside of the key.

Swift: how to retrieve data from firebase?

My structure in firebase is as follows:
app name
user ID
wins = 7
losses = 8
and my code to read the wins child node
ref = Database.database().reference().child(passUserID)
ref?.child("wins").observe(.childAdded, with: { (snapshot) in
//Convert the info of the data into a string variable
let getData = snapshot.value as? String
print(getData)
})
But it prints nothing.
To read data from Firebase you attach a listener to a path which is what creates a FIRDatabase reference. A FIRDatabaseReference represents a particular location in your Firebase Database where there is a key-value pair list of children. So in your case, you have created a Firebase reference to the key "wins" which only points to a value and not a key-value pair. Your reference was valid up to this point:
ref = Database.database().reference().child(passUserID)
//did you mean FIRDatabase and not Database??
This FIRDatabaseReference points to the key passUserID which has a key-value pair list of children ["wins":"7"] and ["losses":"8"] (NOTE: a key is always a string). So from your FIRDatabase reference, you create your observer as follows and read the value of "wins":
ref?.observe(.childAdded, with: { (snapshot) in
//Convert the info of the data into a string variable
if let getData = snapshot.value as? [String:Any] {
print(getData)
let wins = getData["wins"] as? String
print("\(wins)")
}
})
The Child added event will fire off once per existing piece of data, the snapshot value will be an individual record rather than the entire list like you would get with the value event. As more items come in, this event will fire off with each item. So if "losses" is the first record you might not get the value of "wins". Is this what you are trying to achieve? If what you really wanted to know is the value of "wins" at that particular location and to know if this value has ever changed you should use the .value observer as follows:
ref?.observe(.value, with: { (snapshot) in
//Convert the info of the data into a string variable
if let getData = snapshot.value as? [String:Any] {
let wins = getData["wins"] as? String
print("\(wins)") //check the value of wins is correct
}
})
Or if you just wanted to get the know the value of wins just once and you are not worried about knowing if there any changes to it, use the "observeSingleEvent" instead of "observe".
EDIT
I saw your image and now realize you might also have a problem with your reference. Your ref should actually be something like:
ref = FIRDatabase.database().reference().child("game-").child(passUserID)
You have obscured what "game" is but a valid reference to "wins" will include it.
SECOND EDIT
I will add the following so you can properly debug the problem. Use this pattern to observe the value and see if you get an error returned and what is says:
ref.observe(.value, with: { (snapshot) in
print(snapshot)
}, withCancel: { (error) in
print(error.localizedDescription)
})
Normally it will give you an error if you cannot access that Firebase location because of a database rule. It will also be a good idea to see if print(snapshot) returns anything as above.
You need this:
ref.child("YOUR_TOP_MOST_KEY").observe(.childAdded, with: { (snapshot) in
let keySnapshot = snapshot.key
//print(keySnapshot)
self.ref.child(keySnapshot).observe(.value, with: { (snapshot2) in
//print(snapshot2)
}) { (error) in
print("error###\(error)")
}
})

Firebase Sort array of class by value

I'm using Firebase. In my app, I get a child value by passing in a bottleID and get the details for that value from the snapshot. I then assign the details to an object of MyCollection_Class and add it to an array. After getting every single bottle value, I want to sort that array using the created_at tag before reloading the table view. Please advise me on how to sort the array of objects by a specific instance variable.
let Collection = MyCollection_Class()
FireBaseConstants.AUCTIONS_REF.child(bottleID).observeSingleEvent(of: .value, with: { (snap) in
if !(snap.value is NSNull) {
Collection.id = bottle_dict["id"] as? String
Collection.item_number = bottle_dict["item_number"] as? Int
Collection.created_at = bottle_dict["created_at"] as? String
if !(self.MyCollectionsIDArr.contains(Collection.id! as String)) {
self.MyCollectionsArr.append(Collection)
self.MyCollectionsIDArr.append(Collection.id!)
// I want to sort the MyCollectionsArr using created_at here
self.tbl_Latest.reloadData()
}
}
})
You can just retrieve the data already sorted from Firebase by using
queryOrderedByChild.
An example would be:
ref.child(bottleID).queryOrderedByChild("created_at").queryEqualToValue(0).observe SingleEventOfType(.Value, withBlock: { snap in
print("snap \(snap)")
expectation.fulfill()
})

How to update value in Firebase with childByAutoId?

When I create objects in Firebase, I use childByAutoId. How can I update these specific objects later? I'm having trouble obtaining the value of the key Firebase automatically updates. Snapshot.key just returns "users". Here's my JSON structure:
{
"users" : {
"-KQaU9lVcUYzIo52LgmN" : {
"device" : "e456f740-023e-440a"
"name: "Test"
}
},
How can I get the -KQaU9lVcUYzIo52LgmN key? I want to update the device child. Here's what I have so far. It currently creates a completely separate snapshot with a single child.
self.rootRef.child("users").queryOrdered(byChild: "name").queryEqual(toValue: self.currentUser).observeSingleEvent(of: .value, with: { (snapshot) in
let key = self.rootRef.child("users").childByAutoId().key
let childValues = ["device": device]
self.rootRef.child("users").child(key).updateChildValues(childValues)
Edit: device is a string set further up in the code. Not defined in this scope (to make it easier to read for this question).
When you get Snapshot.key, it returns "users" because that is the overall key for your snapshot. Everything inside of "users" in your snapshot is considered the value.
You need to iterate over the child layers to dig down to "device".
Try this:
rootRef.child("users").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if let result = snapshot.children.allObjects as? [FIRDataSnapshot] {
for child in result {
var userKey = child.key as! String
if(userKey == userKeyYouWantToUpdateDeviceFor){
rootRef.child("users").child(userKey).child("device").setValue(device)
}
}
}
})
This code will do the following:
Gets snapshot of your reference (the key for that would be
'users').
Gets all the children (your user keys) and assigns them as another
snapshot to 'result'.
Checks each key one at a time until it finds the key you want (for
example, if you look for user with the key "-KQaU9lVcUYzIo52LgmN",
it will find it on the first iteration in your example code you
posted).
Once it finds that key, it sets the value for the device inside that
key with the line
rootRef.child("users").child(userKey).child("device").setValue(device).
Of course, you will need to store all your user keys when you make them. You can maybe use SharedPreferences on the device for this, but if it gets cleared for any reason then that data will just be sitting there. You could also store it on internal storage for your app, but SharedPreferences is what I would use.
Hope this helps!
snapshot has a property key which is
The key of the location that generated this FIRDataSnapshot.
And as you can see you are getting one (snapshot) by calling observeSingleEvent(of: .value, with: { (snapshot)...
so instead of let key = self.rootRef.child("users").childByAutoId().key
try to call let key = snapshot.key
childByAutoId().key always generates new unique key based on timestamp, that's why you are creating new child, not updating the one you want
Hope that works
I adapted Ryan's answer to my own issue (kinda similar) and figured out a way to update your device ID directly without needed to know/store the AutoID key generated by Firebase :
reference = Database.database().reference().child("users")
reference.observeSingleEvent(of: .value, with: { (snapshot) in
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
if child.childSnapshot(forPath: "device").value as? String == self.yourDeviceIDVariable {
print("### Device exists in Firebase at key = \(child.key)")
let childKey = child.key
self.reference.child(childKey).child("device").setValue(yourNewDeviceID)
}
}
}
})

Retrieving Data using Firebase Swift

I'm using Firebase for my Swift iOS application. I found the retrieving data tutorial on Firebase's guide a bit confusing and I am not sure why when I try to access existing values in my database results in nil values.
This is what I have so far:
usersRef.queryOrderedByChild("fbid").queryEqualToValue(userId).observeSingleEventOfType(.Value, withBlock:{ snapshot in
print("SNAPSHOT: ",snapshot.value)
Here is the result of printing snapshot.
SNAPSHOT: {
1 = {
fbid = 1;
firstName = Michelle;
friendlist = {
9 = "Kevin C";
};
lastName = C;
profilepicurl = "https:;
uid = "facebook:1";
};
}
However, the line below results in:
fatal error: unexpectedly found nil while unwrapping an Optional value
firstName = snapshot.value.objectForKey("firstName") as! String
I would like to retrieve all the values for the user (firstName, profilepicurl, friendlist, etc) and store them in variables. It seems simple but perhaps I'm missing something. Any help would be appreciated.
Your FDataSnapshot does not contain a child firstName. It only contains a child 1.
This is because you're performing a query and then asking for a value. Since a query can have many results, it returns a list of results. Even when there's only one result, it is still a list of 1.
The solution is to loop over the children:
usersRef.queryOrderedByChild("fbid")
.queryEqualToValue(userId)
.observeSingleEventOfType(.Value, withBlock:{ snapshot in
for child in snapshot.children {
print("Loading group \(child.key!)")
}
})

Resources