Request result structure firebasedatabase Swift - 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

Related

Snapshot listener, get the document name, Firebase Swift

I am adding a snapshot listener to a firebase Collection/Document/Collection to get the data as it is updated
The issue I am having is I run through a for loop and as it gets the i in the for loop I then use that string (collection name), to direct the snapshot listener, when the data comes in it will add to the data already there rather than change the data. because it doesn't touch the code to get the collection name, as far as I know.
What I need to do is to be able to add the data to a dictionary that has [String:Any], so I can have the ["collection name":name, "document name": document id, "info":document data], this is fine on first run but when data is changed it only get the data from the changed listener but I don't know how I can get it to get the collection name so I can remove that document from the dictionary before adding the new data.
func getClockOutData(completion: #escaping (_ finished: Bool) -> ()) {
for i in projectsList {
Firestore.firestore().collection("Projects").document(i).collection("TimeSheets").addSnapshotListener { (snapshot, err) in
if let err = err {
print("Tony the error was \(err.localizedDescription)")
} else {
print("Tony dataC A get is \(i)")
if let projDocs = snapshot?.documents {
print("Tony dataC get is \(i)")
for d in projDocs {
let dataG = d.data()
let dateG = d.documentID
let dataCT = ["project":i, "dateG":dateG, "inf":dataG] as [String : Any]
print("Tony dataC is \(dataCT)")
self.dataC.append(dataCT)
}
completion(true)
}
}
}
}
}
How can I get the project name (i) when the snapshot fires again with the changes?
I want that so I can create a for loop to check the project and add all the data from the project to a dict with all the same project name grouped and then run through that loop when there are changes to remove the project info before it is re appended
When you're retrieving the documents from the subcollection, no data from the parent document is retrieved.
So you will either have to load the parent document to get that information, or get the information from the context where you make this request (as likely you've loaded the parent document before as part of determining projectsList).

I need help getting values out of a snapshot from firebase

My Data in Firebase looks like this:
I am retrieving the data with
var ref: DatabaseReference
ref = Database.database().reference()
ref.child("peopleReminders").child("-LBO0TMbOM0cwd5TMMiP").observe(.value) { snapshot in
for value in snapshot.children {
print(value)
}
}
This prints the following:
Snap (0) r04
Snap (1) r02
Snap (2) r01
This should be an array. What I need to do is get the values R04, R02, and R01 out of it.
You don't need a loop or enumeration to parse it into an array as Firebase supports both Array and Dictionary. You can directly cast the result into an array. Below piece of code will work:
ref.child("peopleReminders").child("-LBO0TMbOM0cwd5TMMiP").observeSingleEvent(of: DataEventType.value) { (snapshot) in
if snapshot.exists() {
let result: [String] = snapshot.value as? [String] ?? []
print(result)
}
}
Note: Always check snapshot existence it will be false if data doesn't exist for the requested reference. Use observeSingleEvent when you need to fetch the data for once if you use observe the call back will be call many times whenever there is a change in requested node so that delete, insert, update etc.
I replaced for value in snapshot.children with the following code.
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
print(rest.value!)
}
The new output is now:
R04
R02
R01

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

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)))]
}

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)
}
})

Getting only first object from Firebase Snapshot Swift

So this is my Firebase Structure:
I'm trying to get all books pictures (bookImage), add them to list and then use this list to fill a table or anythings else. (I'm using swift 3)
struct item {
let picture: String!}
var items = [item]()
func getLatestAddedItems(){
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Items").observe(.childAdded, with: {
FIRDataSnapshot in
let picture = (FIRDataSnapshot.value as? NSDictionary)?["bookImage"] as? String ?? ""
//self.items.insert(item(picture: picture), at: 0)
self.items.append(item(picture: picture))
print(self.items[0].picture)
print(self.items[1].picture) // error here
})}
I'm able to see the first print output but on the second one I'm getting fatal error: Index out of range even I have 3 books on my database.
Since your using .childAdded, it iterates through that closure for each object in the data tree, in this case, each book. When you try to print the second picture, its still in its first iteration. Meaning you only have retrieved the first book so far. That's why you can print the first book item but not the second one. If you moved the print statements outside of the closure, and then did the print statements after the closure iterated over all three books, you wouldn't get the error.
Don't change it to .value unless if every time a new one is subsequently added you want to get the entire list of books all over again. If its a large amount of books, it will be a lot of data to go through each time.
Summary: .childAdded gives you one book at a time, with a new snapshot for each one. .value gives you all the books in one snapshot, then you must iterate over them yourself in the closure. ex.
for snap in snapshot.children {
// now you can do something with each individual item
}
also I just noticed your using the FIRDataSnapshot type in your closure, that should be a variable which represents the snapshot you received, not the type itself. Change "FIRDataSnapshot in" to something like "snapshot in" snapshot is a representation of what information was given to you by the observe closure, in this case, an object with a type of FIRDataSnapshot.
Edit:
Your solution you mentioned below works fine, but I'll add an alternative that is cleaner and easier to use.
add an init method to your Book class that takes a FIRDataSnapshot as the init parameter, then init the object when you query Firebase:
struct Book {
let bookImageString: String
init?(snapshot: FIRDataSnapshot) {
guard let snap = snapshot.value as? [String : AnyObject], let urlString = snap["bookImage"] else { return nil }
bookImageString = imageString
{
{
then when you query firebase you can do this:
for snap in snapshot.children {
if let snap = snap as? FIRDataSnapshot, let book = Book(snapshot: snap) {
self.items.append(book)
{
}
doing it this way cleans up the code a little bit and leaves less chance of error in the code.
Also, since your using .value, make sure to empty the data source array at the beginning of the closer, or else you will get duplicates when new books are added.
items.removeAll()
Finally I'm posting the solution:
func getLatestAddedItems(){
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("Items").observe(.value, with: {
snapshot in
//self.items.insert(item(picture: picture), at: 0)
for childSnap in snapshot.children.allObjects {
let snap = childSnap as! FIRDataSnapshot
print(snap.key)
let picture = (snap.value as? NSDictionary)?["bookImage"] as? String ?? ""
print(picture)
}
})
}