How do I retrieve a random object from Firebase using a sequential ID? - swift

I'm looking for an easy way to query my database in firebase using swift to retrieve a random object. I've read a lot of threads and there doesn't seem to be an easy way. One example showed it can be done be creating a sequential number but there's no information on how to create this sequential number for each record.
So either I need information on how to create a sequential number each time a record is created or if someone knows an easy way to retrieve a random record from a database that would be very helpful. In swift preferably.
My Database structure:

QUERY RANDOM OBJECT IN FIREBASE < VERY SIMPLE SOLUTION > SWIFT 4
One thing that you could try is to restructure your data like this:
- profiles
- 1jon2jbn1ojb3pn231 //Auto-generated id from firebase.
- jack#hotmail.com
- oi12y3o12h3oi12uy3 //Auto-generated id from firebase.
- susan#hotmail.com
- ...
Firebase's auto-generated id's are sorted in lexicographical order by key, when they are sent to Firebase, so you can easily create a function like this:
func createRandomIndexForFirebase() -> String {
let randomIndexArray = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]`
let randomIndex = Int.random(in: 0..<randomIndexArray.endIndex)
//In lexicographical order 'A' != 'a' so we use some clever logic to randomize the case of any letter that is chosen.
//If a numeric character is chosen, .capitalized will fail silently.
return (randomIndex % 2 == 0) ? randomIndexArray[randomIndex] : randomIndexArray[randomIndex].capitalized
}
Once you get a random index you can create a firebase query to grab a random profile.
var ref: DatabaseReference? = Database.database().reference(fromURL: "<DatabaseURL>")
ref?.child("profiles").queryOrderedByKey().queryStarting(atValue: createRandomIndexForFirebase()).queryLimited(toFirst: 1).observeSingleEvent(of: .value, with: { snapshot in
//Use a for-loop in case you want to set .queryLimited(toFirst: ) to some higher value.
for snap in snapshot.children {
guard let randomProfile = snap as? DataSnapshot else { return }
//Do something with your random profiles :)
}
}

Database.database().reference().child("profiles").observeSingleEvent(of: .value) { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
// do random number calculation
let count = snapshots.count
return snapshots[Int(arc4random_uniform(UInt32(count - 1)))]
}

Related

Request result structure firebasedatabase Swift

Looking to retrieve value of custom class from a snap in swift like i do in java , i use Firebasedecoder .
Works fine but i need the following structure
{
username = uiii;
email = test#rom.com
..}
If i make ordered requests like .queryOrdered(ByCHild:email).queryEqual("uiii"), i get the resquest with a previous node :
{
"hjhj"= {
username = uiii;
email = test#rom.com
..} }
Looking for a way to either remove the uneccessary values or to have the correct snap structure.
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.
To get to the individual node(s) in the result, you need to loop over snapshot.children, as shown in the Firebase documentation on listening for value events on a list of children.
Also see:
previous questions about looping over children
Get the data from all children in firebase using swift
Firebase queryOrderedByChild() method not giving sorted data for an alternative if you want to only receive a single child node and only once
In short, if you have extra data at the same level and that makes decodeFirebase crash, you still can use it:
let value = snapshot.value
let modifiedValue:NSMutableDictionary = (value as AnyObject).mutableCopy() as! MutableDictionary
You then can remove elements by key: modifiedValue.removeObject(forKey: test)
and then apply decode.
custom class USER with all values in the pictures
import Foundation
import SwiftUI
import Firebase
import CodableFirebase
//knowing the userid , clean beautiful result with Firebasedecoder
func cleanResultWithCodebableFirebase(){
ref.child("3oleg").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value else { return }
do {
let user = try FirebaseDecoder().decode(User.self, from: value)
print(user.getUser_id())
} catch let error {
print(error)
}
})
}
not knowing userID dirty result
func customwithdirtylists(){
let query = ref.queryOrdered(byChild: Strings.field_username).queryEqual(toValue: "uiiii")
query.observeSingleEvent(
of: .value, with: { (snapshot) -> Void in
for child in snapshot.children {
let childSnapshot = snapshot.childSnapshot(forPath: (child as AnyObject).key)
for grandchild in childSnapshot.children{
let grandchildSnapshot = childSnapshot.childSnapshot(forPath: (grandchild as AnyObject).key)
//possible from here to get the key and values of each element of the custom class
}
}
})
}
This is the code i use in both cases, direct request or when ordered . No list visible when direct with the help of firebase decode .Ugly way to rebuild custom class thru looping . I m sure there are more elegant ways to do it especially when all i need is just remove one value of the direct result to have a clean result

Firebase query retrieves data in a random order. The data is organised by autoID

I've got the following database structure:
PVV
-- AutoID
- Data1
- Data2
- Status: Active
- ImageName: Path\FirebaseStorageImage.jpg
I'd like to retrieve the data in chronological order, and then sort the data in a descending manner (most recent first).
I think autoID does use a combination of date and time, and Firebase does normally retrieve the data in a fixed order. I am using the same function as below to retrieve text data (that does not have an imageName), and that works fine.
However, the function below returns data in a random order:
func LoadDataFromImageTest() {
self.ImageList.removeAll()
self.ImageTestFromFBTableView.reloadData()
databaseReference = Database.database().reference()
let refPVV = Database.database().reference(withPath: "PVV").queryOrdered(byChild: "Status").queryEqual(toValue: "Active")
refPVV.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in
//if the reference have some values
if snapshot.childrenCount > 0 {
//clearing the list
self?.ImageList.removeAll()
//iterating through all the values
for PVV in snapshot.children.allObjects as! [DataSnapshot] {
//getting values
let PVVObject = PVV.value as? [String: AnyObject]
// let PVVText = PVVObject?["ImageString"]
let PVVName = PVVObject?["Name"]
let PVVBodyText = PVVObject?["BodyText"]
let PVVValue = PVVObject?["PVVValue"]
let Key = PVV.key
let PVVImageName = PVVObject?["ImageName"] as! String?
let imageURL = Storage.storage().reference().child(PVVImageName!)
imageURL.downloadURL(completion: { (url, error) in
if error != nil {
print(error?.localizedDescription as Any)
return
}
PVVurlName = url
let PVV = ImageModel(Name: PVVName as!String?, BodyText: PVVBodyText as! String?, PVVValue: PVVValue as! String?, Key: Key as String?, ImageName: PVVurlName as URL?)
self!.ImageList.insert(PVV, at: 0)
self?.ImageTestFromFBTableView.reloadData()
})
}
}
}
)}
I set a debug point right before I start downloading the URL. Each time I run, it returns values for PVVObject in a different order.
I have another tree like this:
Challenges
- AutoID
- Data1
- Data 2
- Status: Active
I've recycled the function above to retrieve data from the above tree, and I always get the data in the same order, when setting a debug point in the same place.
What am I doing wrong?
As per Firebase documentation the downloadURL method is asynchronous. It means that the order in which the downloaded files are retrieved is not guaranteed. When you are in the completion block of the downloadURL method, you have no idea to which PPV object the image belongs to.
I think the best is to change the architecture of your code. Create an object model class for PPV, with a imageUrl property (which is attached to each instance), and trigger the download job when you observe a change in value of this property (in the didSet method for instance). This way you will be sure that the downloaded file belongs to the instance.

How can I read the value of a Firebase database dictionnary created using .childByAutoId() in Swift?

I have a dictionary of dictionary of Strings stored in a Firebase database. It can be seen below.
As you can see, each entry is created using .childByAutoId() and contains two variables: text and tag.
I wish to be able to go through all of the entries, and compare the value of text with a variable saved locally in my app. I have tried many ways, but cannot find any solution that works. How should I proceed?
Thank you in advance for your help.
You need to observe database at specific reference and then convert a snapshot that will be send to you. The snapshot represents a fragment of your database at given path
let dbRef = Database.database().reference().child("messages")
dbRef.observeSingleEvent(of: .value) { (snapshot) in
for message in snapshot.children{
let msg = (message as! DataSnapshot).value //message as snapshot
//now you need to cast it to your structure([String:String])
let projectObj = Message(snapshotChild: msg as! [String:String])
//and do your comparison
}
}

Get youngest child in Firebase users

I want to find the youngest user in my list of users and load their data: name, profile pict, and current job assignments. I have read the Firebase primer on querying data, but their examples don't work for me because my data is organized differently. I have an additional child layer.
This is my JSON tree in Firebase:
I've tried loading the list of users and then iterating over them to find the youngest user, but that seems like overkill. The Firebase documentation makes me think I should be able to do the query through a Firebase method, like 'queryOrderedByChild' or similar.
I've gone over the old documentation here and the new documentation here, but I'm still left wondering what to do.
So this is my workflow:
The app will find the youngest user in the list of "members" and load their name, profile pict, birthday, etc. They will choose from a list of jobs. Once that user has chosen from the lists of available jobs, the app will load the next youngest user from the list of "members", and so on until all users have been loaded and have been given the chance to select jobs.
I think a better workflow would be this:
Get youngest user by utilizing a Firebase query
Use that query to load that user (image and name)
How would I go about doing that?
EDIT #1: Code I've Tried
func loadExistingUsers(completion: #escaping ([[String : Any]]) -> ()) {
var dictionary = [[String : Any]]()
ref.child("members").observeSingleEvent(of: .value) { (snapshot: FIRDataSnapshot) in
for child in snapshot.children {
let snap = child as! FIRDataSnapshot
if let value = snap.value as? [String : Any] {
dictionary.append(value)
}
}
completion(dictionary)
}
}
And then in ViewDidLoad:
loadExistingUsers { (dictionary) in
var youngestBirthday = 19000101
var userName = "Sophie"
for item in dictionary {
let fetchedBirthday = item["birthday"] as! Int
let fetchedName = item["firstName"] as! String
if fetchedBirthday > youngestBirthday {
youngestBirthday = fetchedBirthday
userName = fetchedName
}
}
print(userName,youngestBirthday)
}
This method returns the youngest user from my list of users, but it seems like an awfully long way to go to get what I want. I have to first fetch the users from Firebase, and then parse the snapshot, then create an array, then sort the array, then get the user name. I was under the impression Firebase could do all that with one query. Am I wrong?
You can get the youngest child using this code: (since your youngest date is the largest number so I am using toLast)
ref.child("members").queryOrdered(byChild:"birthday").queryL‌​im‌​ited(toLast: 1).observeSingleEvent(of: .childAdded, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
let name = value["firstname"] as? String
//you can do for other values as well
print(name)
}
})

Value not being removed from dictionary

I have a dictionary in Firebase called peopleWhoLike, the key is an auto-id and the value is the uid of the user who liked, I'm trying to loop through the peopleWhoLike dictionary and find the entry with the current users uid as the value so that I can remove it, but the value is not being removed.
func removeLike(postID: String){
ref.child("posts").child(postID).observe(.value, with: { (snapshot) in
if let info = snapshot.value as? [String : AnyObject]{
if var peopleWhoLike = info["peopleWhoLike"] as? [String : String]{
print("peopleWhoLike - \(peopleWhoLike)")
for person in peopleWhoLike{
if person.value == FIRAuth.auth()!.currentUser!.uid{
peopleWhoLike.removeValue(forKey: person.key)
print("personkey - \(person.key)")
}
}
}
}
})
}
Both print statements print correctly, ie person.key being the correct key
Any help would be much appreciated thanks!
you have only an snapshot (a copy) of the data there
to remove the value from the firebase database;
try this:
//path should be like this (i guess):
let currentUserUid = FIRAuth.auth()!.c‌​urrentUser!.uid
ref.child("posts")
.child(postId)
.child("peopleWhoLike")
.chi‌​ld(currentUserUid)
.rem‌​oveValue()
or same:
let currentUserUid = FIRAuth.auth()!.c‌​urrentUser!.uid
ref.child("posts/\(postId)/peopleWhoLike/\(currentUserUid)").rem‌​oveValue()
UPDATE
you you like to remove the person key - then you can:
a) iterate over peopleWhoLike and find if it is the user ( but please put this let currentUserUid = FIRAuth.auth()!.c‌​urrentUser!.uid outside the loop!
//path should be like this (i guess):
let currentUserUid = FIRAuth.auth()!.c‌​urrentUser!.uid
// loop and when match `person.value == currentUserUid` then:
ref.child("posts")
.child(postId)
.child("peopleWhoLike")
.chi‌​ld(person.key) //<-- change here
.rem‌​oveValue()
b) you search in the query. and remove then the resulting node.
ref.child("posts")
.child(postId)
.child("peopleWhoLike")
.startAt(currentUserId)
.endAt(currentUserId)
. [...] do something
i dont know if you can direct call .removeValue() at this point. but with a SingleEvent and an snapshot you can do snapshot.ref.removeValue() - doublecheck before you delete. but since this results in a reference you should direct able to call .removeValue()
ref.child("posts")
.child(postId)
.child("peopleWhoLike")
.startAt(currentUserId)
.endAt(currentUserId)
.removeValue()
note: this search takes longer than a direct path
see here doc for query:
https://firebase.googleblog.com/2013/10/queries-part-1-common-sql-queries.html#byemail
https://firebase.google.com/docs/database/ios/read-and-write#delete_data
NOTE:
i would advice you to save it with the userUid as key because you only need a onliner to delete (see my first codesnippet where you not need to get all data from peopleWhoLike) and as value just 1 or you can save the current date (then you know when it was liked)