Retriving data from database - swift

First time asking a question here, so sorry if I do it wrong.
Anyways. I'm using Firebase Database to store "Results" in my Quiz app. When data is store it looks like this
Results
-LjQ34gs7QoL1GMufiMsaddclose
Score: xx
UserName: xx
-LjQ3NeCoDGob8wnhstH
Score: xx
UserName: xx
I would like to access score and username from it and display it in a HighScore tableview. Problem is - I can get the "Results" node, but because of the id of the results (ie LjQ34gs7QoL1GMufiMsaddclose) I don't know how to access the score and username.
I got the data snapshot​, but not sure how to "bypass" the id to get to score and username.
Hope I made it at least a bit clear, what the problem is.
let ref = Database.database().reference().child("Results")
ref.observe(.value) { (DataSnapshot) in
print(DataSnapshot.value as Any)
}

You current code gets you a single snapshot with the results of all users. You'll need to loop over the child snapshots to get the result of each user, and then look up their specific properties with childSnapshot(byName:):
let ref = Database.database().reference().child("Results")
ref.observe(.value) { (snapshot) in
for case let userSnapshot as DataSnapshot in snapshot.children {
print(userSnapshot.childSnapshot(forPath: "Score").value)
}
}
Also see:
How to get all child data from firebase without knowing the path
Iterate through nested snapshot children in Firebase using Swift
How do I loop through and get all the keys of the nested nodes in firebase?
Retrieving Data using Firebase Swift
And probably some more from this list.

Related

How to know which index a child was added to an ordered FirebaseQuery

Using Firebase and Swift SDK
I just started with Firebase and wanted to display a list of Conversations ordered by last_update. The following query works fine :
let query = Database.database().reference().child("chat").child("channels").queryOrdered(byChild: "last_update")
query.observe(DataEventType.childAdded) { (snapshot: DataSnapshot) in
if let value = snapshot.value as? [String: Any?] {
log.debug("added channel: \(snapshot.key) : \(value)")
//add object to array, insertRow in tableview
}
}
The first time my view is loaded, each item arrives in the correct order specified by the query, so the display is ok. But if I create a new channel, it does appear at the end of the tableview, because I just add it at the end of the array and just call insertRow on my table view. My question is : is there any mecanism that give us the new inserted position of the DataSnapshot ?
Same question for DataEventType.childMoved : we get to know that a snapshot has moved, but how to know where it has moved ??
I finally end up using FirebaseUI, especially the submodule FirebaseDatabaseUI (see the github repo here)
They provide a FUITableViewDataSource, you just create a query and pass it to it, it will handle everything like sorting etc. I also used FUIArray, which is simply an array backed by a query, with a delegate for added / deleted / moved / update events (and proper indexes).

Firebase: query and delete keys in a range

If I have some data in the Firebase real-time database like this
root--.
|--stuff--.
|--1
|--2
|--3
|--4
|--5
|--6
|--7
where all those numbers are keys that contain more data, and I want to delete all the keys less than or equal to 4, how do I do that without downloading the entire "stuff" branch?
With swift I think I can query the keys in that range by
let ref = FIRDatabase.database().reference().child("stuff")
let query = ref.queryEnding(atValue: 4)
but I don't know how to retrieve the key names themselves so that I can delete them sort of like this pseudocode
for key in queryResults {
let ref = FIRDatabase.database().reference().child("stuff/\(key)")
ref.setValue(nil)
}
(In reality I'm dealing with timestamp keys and I want to delete data branches that have gotten too old.)
Not sure how to do it in swift, but you can do it with orderByKey.
.database().reference()
.child("stuff")
.orderByKey()
.startAt(STARTING_TIMESTAMP)
.endAt(ENDING_TIMESTAMP)
And then loop over the ids of the corresponding result.
This will download all information for those children though. If you don't want that you'll have to store it somewhere else to easily delete it.
Sample Swift 3 / Firebase 2.x code to get the keys from a snapshot
for child in (snapshot?.children)! {
let snap = child as! FDataSnapshot
print(snap.key)
}

One to many query with Firebase and Swift 3

I have an app, that has Topics and categories, one category may belong to one or more topics, i'm trying to filter the data to only show category that has a certain topic id marked as true, this is the structure:
Here is the code i come up with ( which was working on Swift 2.3 ):
self.ref = FIRDatabase.database().reference(fromURL: FIREBASE_URL).child("categories")
let query = ref?.queryOrdered(byChild: "topics/idt2").queryEqual(toValue: true)
query!.observe(.value, with: { (snapshot) in
//This should bring back both categories, Soccer and Moon
print("Inside query \(snapshot.value)") // Prints null
})
Any ideas?
When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
You will need to handle this list in your callback block:
query!.observe(.value, with: { (snapshot) in
for child in snapshot.children {
print(child.key)
}
})
Also see:
Firebase remove snapshot children swift
Grabbing information out of a query with Firebase and Swift
Retrieving Data using Firebase Swift
Firebase complex query clauses
Firebase snapshot.key not returning actual key?

Querying in Firebase by child of child

I have a structure of objects in Firebase looking like this:
-KBP27k4iOTT2m873xSE
categories
Geography: true
Oceania: true
correctanswer: "Yaren (de facto)"
languages: "English"
question: "Nauru"
questiontype: "Text"
wronganswer1: "Majuro"
wronganswer2: "Mata-Utu"
wronganswer3: "Suva"
I'm trying to find objects by categories, so for instance I want all objects which has the category set to "Oceania".
I'm using Swift and I can't really seem to grasp the concept of how to query the data.
My query right now looks like this:
ref.queryEqualToValue("", childKey: "categories").queryOrderedByChild("Oceania")
Where ref is the reference to Firebase in that specific path.
However whatever I've tried I keep getting ALL data returned instead of the objects with category Oceania only.
My data is structured like this: baseurl/questions/
As you can see in the object example one question can have multiple categories added, so from what I've understood it's best to have a reference to the categories inside your objects.
I could change my structure to baseurl/questions/oceania/uniqueids/, but then I would get multiple entries covering the same data, but with different uniqueid, because the question would be present under both the categories oceania and geography.
By using the structure baseurl/questions/oceania/ and baseurl/questions/geography I could also just add unique ids under oceania and geography that points to a specific unique id inside baseurl/questions/uniqueids instead, but that would mean I'd have to keep track of a lot of references. Making a relations table so to speak.
I wonder if that's the way to go or? Should I restructure my data? The app isn't in production yet, so it's possible to restructure the data completely with no bigger consequences, other than I'd have to rewrite my code, that pushes data to Firebase.
Let me know, if all of this doesn't make sense and sorry for the wall of text :-)
Adding some additional code to Tim's answer for future reference.
Just use a deep query. The parent object key is not what is queried so it's 'ignored'. It doesn't matter whether it's a key generated by autoId or a dinosaur name - the query is on the child objects and the parent (key) is returned in snapshot.key.
Based on your Firebase structure, this will retrieve each child nodes where Oceania is true, one at a time:
let questionsRef = Firebase(url:"https://baseurl/questions")
questionsRef.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)
.observeEventType(.ChildAdded, withBlock: { snapshot in
print(snapshot)
})
Edit: A question came up about loading all of the values at once (.value) instead of one at at time (.childAdded)
let questionsRef = Firebase(url:"https://baseurl/questions")
questionsRef.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)
.observeSingleEventOfType(.Value, withBlock: { snapshot in
print(snapshot)
})
Results in (my Firebase structure is a little different but you get the idea) uid_1 did not have Oceania = true so it was omitted from the query
results.
Snap (users) {
"uid_0" = {
categories = {
Oceania = 1;
};
email = "dude#thing.com";
"first_name" = Bill;
};
"uid_2" = {
categories = {
Oceania = 1;
};
"first_name" = Peter;
};
}
I think this should work:
ref.queryOrderedByChild("categories/Oceania").queryEqualToValue(true)

Saving and/or Querying User Display Names in Firebase using caseInSensitive?

I'm moving my project over to Firebase from Swift. Firebase user's don't have usernames but I'm allowing them to save a display name which works more like a attribute than an actual object. How can I get users to query for other users/"friends" using case in sensitive text?
You can easily accomplish this task. We don't know how your current data is structured but here's an example
users
user_id_0
dsplay_name: "Smokey"
lower_case_name: "smokey"
user_id_1
display_name: "Bandit"
lower_case_name: "bandit"
When you create a user in Firebase, create a node in the /users node with their uid as the node name, and then display_name and lower_case_name as children
When you write your data to the users node, just lower case the display name when you are writing to the lower_case_name child:
let lowerCaseString = displayNameString.lowercaseString
let userRef = the users uid
let userData = ["display_name": "Bandit", "lower_case_name": lowerCaseString]
userRef.setValue(userData)
Then you query on the lower_case_name child using a lower case string.
ref.queryOrderedByChild("lower_case_name").queryEqualToValue("bandit")
.observeEventType(.ChildAdded, withBlock: { snapshot in
}