Access nested Firebase data in Swift - ios

I am trying to access nested data values based on profile uid. with this One profile based on uid has multiple friends and friends have location and profile image (in profile) object.
I am using CodableFirebase and models for Friend,Profile,LastLocation. for main data list I use observeSingleEvent(of: .value and for all other nested data I need to use .observe(.childAdded .
Second thing How would I read all values based on profile uid and save them into an Array that I can then display in a TableView ?
I am not getting an idea how to do that please anyone help me.
Firebase Database:
zzV6DQSXUyUkPHgENDbZ9EjXVBj2
 friends
 FTgzbZ9uWBTkiZK9kqLZaAIhEDv1
 conversationUid: "-L_w2yi8gh49GppDP3r5"
 friendStatus: "STATUS_ACCEPTED"
 notify: true
 phoneNumber: "+055441503"
 uid: "FTgzbZ9uWBTkiZK9kqLZaAIhEDv1"
 HHCBYyP4KybcINgZaWVukR967l12
 IcBfQKAnswQ7rivFuZsPvRUcsX43
 LEZ2FUxS1WVaqSEHx5YqnpZ5Els1
 YyFIm4hTorMFq4olpvHJI7e5e3a2
 nkS2cjSvmLUEYEKsAVyErz9HkrJ2
 lastLocation
 batteryStatus: 27
 latitude: 41.0220811
 longitude: 29.0445012
 timeStamp: 1571742057730
 uid: "zzV6DQSXUyUkPHgENDbZ9EjXVBj2"
 profile
 fcmToken: "enMneewiGgg:APA91bHyA4HypWUYhxGTUTTcWch8ZJ_6UUW..."
 name: “My Profile name”
 phoneNumber: "+05588674"
 picture: "profile/zzV6DQSXUyUkPHgENDbZ9EjXVBj2/a995c7f3-7..."
 uid: "zzV6DQSXUyUkPHgENDbZ9EjXVBj2"
Code:
let ref = Database.database().reference()
ref.child("users").child("zzV6DQSXUyUkPHgENDbZ9EjXVBj2").observeSingleEvent(of: .value, with: { (snapshot) in
for userSnapshot in snapshot.children.allObjects as! [DataSnapshot] {
let friendsSnapshot = userSnapshot.childrenCount
do {
let frndList = try FirebaseDecoder().decode(Friend.self, from: friendsSnapshot)
self.AppData = [frndList]
print(frndList)
self.tableView.reloadData()
} catch let error {
print(error)
}

Related

iOS Firebase -How to remove children with same key from different nodes

I have a ref named Following. Under that ref there are 2 different userIds who are following the same user. If the user they are both following wants to delete their account I want to delete them from the Following node. Multi location update doesn't seem correct to achieve this.
How can it be done?
User kk8qFOIw... is the user who is deleting their account. Once deleted their keys should be removed from the other user's nodes.
This is how you can do it :
First get all the nodes where your id = 1 , then run a multipath update and set them to empty.
let userId = "yourUserId"
self.ref.child("following").queryOrdered(byChild: userId).queryEqual(toValue: 1).observeSingleEvent(of: .value) { (snasphot) in
guard let value = snasphot.value as? [String : Any] else {return}
var multipathUpdate = [String:Any]()
value.keys.forEach({ (key) in
multipathUpdate["following/"+key+"/"+userId] = [:]
})
self.ref.updateChildValues(multipathUpdate, withCompletionBlock: { (err, ref) in
})
}

Firebase - nested observeSingleEvent methods inside for loop Swift

I have been trying to fetch data from Firebase using Realtime database. I want to check the contacts in iPhone and then if any contact number matches with that of any number in "numbers" table in db, then I have to get the user_key from it and then using that key, I have to obtain the corresponding details from users table.
for number in numbers {
Database.database().reference().child("numbers/\(number)").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
let userKey = snapshot.value as! String
// We found this user, no determine there name, (TODO has_image?)
Database.database().reference().child("users/\(userKey)/public/name").observeSingleEvent(of: .value, with: { (namesnapshot) in
if namesnapshot.exists() {
let name = namesnapshot.value as! String
print("FOUND \(name)")
complete(.success((userID: userKey, name: name)))
}
})
} else {
if numbers.index(of: number) == numbers.count - 1 { // Last Number checked and not found yet, so fail
complete(.failure(UserApiError.UserNotFound))
}
}
})
}
numbers is the array of contact numbers for a particular contact. For a contact having single number, this works fine. But for contacts having multiple numbers, the
Database.database().reference().child("users/\(userKey)/public/name").observeSingleEvent(of: .value, with: { (namesnapshot) in
will call after some time due to which the next index in the for loop gets called. So even if I have the data in first number in a contact, it will return failure because the next number will be iterated before the success of the observeSingleEvent.
I have been sitting for hours now, no ideas left with me. Please help!
I think a better approach is:
1 - Get all numbers from DB.
2 - Get all contact numbers that exists on DB.
3 - Finally, get the name of that contacts.(Exactly the way you are doing).
OBS: To do that you must change your DB. Your numbers must be saved as key-value pair. For exemple "555-0000" : true.
Database.database().reference().child("numbers").observeSingleEvent(of: .value, with: { (snapshot) in
guard let numbersFromDB = snapshot.value as? [String: Any] else{
print("Fail get numbers from db")
}
let numbersMatchedOnDB = numbersFromDB.keys.filter{ numbers.contains($0) }//get numbers from contact that exist on DB.
if numbersMatchedOnDB.isEmpty{
complete(.failure(UserApiError.UserNotFound))
}
else{
//For each contact number that exist on DB. it gets its name.
numbersMatchedOnDB.forEach{ numberMatchedOnDB in
Database.database().reference().child("numbers/\(numberMatchedOnDB)").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
let userKey = snapshot.value as! String
// .... nothing changed here ....
}
})
}
}
})

iOS Firebase get data where value equals to something

How can I get data in swift3 from firebase where the value equals something.
In MySQL that would be "SELECT * FROM users WHERE type = 'brown'"; how can I write that where part in Firebase?
I have this DB
And I want to get all the data from it where month == currentMonth (I have a currentMonth variable)
You can Query like this:
let ref = Database.database().reference()
ref.child("Budget")
.queryOrdered(byChild: "Month")
.queryEqual(toValue: "November")
.observe(.value, with: { (snapshot: DataSnapshot) in
var array:[Budget] = []
for budgetChild in snapshot.children {
let budgetSnapshot = budgetChild as! DataSnapshot
let budgetDictionary = budgetSnapshot.value as! [String:String]
let budget: Budget = Budget()
budget.description_ = budgetDictionary["Description"]
budget.month = budgetDictionary["Month"]
budget.type = budgetDictionary["Type"]
budget.value = budgetDictionary["Value"]
array.append(budget)
}
})
As far as I'm aware firebase realtime database doesn't have support for querying like this. Instead you need to call down the whole ref 'budget' then filter it locally in your app.
FireStore apparently has better query support although its in Beta.

Storing posts (image, caption) in Firebase database with auto increment Swift 3

I am trying to store user posts in my firebase data like the following:
I have successfully stored each image in storage with the following code:
let storage = FIRStorage.storage()
let data = UIImagePNGRepresentation(postImage!)
// guard for user id
guard let uid = FIRAuth.auth()?.currentUser?.uid else {
return
}
let photosRef = storage.reference().child("posts")
let imageName = NSUUID().uuidString
let photoRef = photosRef.child("\(uid)")
photoRef.child("\(imageName)").put(data!, metadata: nil){(metaData,error) in
if let error = error {
print("there was an error")
print(error.localizedDescription)
return
}else{
// store downloadURL
let downloadURL = metaData!.downloadURL()!.absoluteString
let values = ["uid": uid, "caption": caption, "download_url": downloadURL]
// store downloadURL at database
let databaseRef = FIRDatabase.database().reference()
// store values in posts/post_1 (post_1..post_2 etc)
}
}
However, I'm having trouble storing the downloadURL (values array) in my posts database because I can't figure out how to have an incremental value for post 1 post 2 post 3 etc etc
Is there a better way to store the posts in the posts database without needing incremental values?
Appreciate any help.
Thanks!
There are a number of ways to store incremental values - it all depends on how you want the values stored and how you will be retrieving and using them.
The general process is to create parent nodes using childByAutoId - Firebase generates the distinct node names for you.
let ref = rootNode.childByAutoid()
ref.setValue("my value")
This will result in nodes with a Firebase generated key
root_node
-Ynaosdokasodkpasd: "my Value"
That being said, it's hard to order them in a meaningful way, so you may want to add nodes that will enable you to specify the order
let ref = rootNode.childByAutoid()
let dict = ["text": "my value", timeStamp: "20161207131600"]
ref.setValue(dict)
which results in
-Ynaosdokasodkpasd
text: "my value"
timeStamp: "20161207131600"
you can then use the timeStamp to order your data in Firebase queries.

Setting a class's values with multiple queries

I've got a class that has Title, users and a timestamp.
My database is set up like :
users
ablksjdlf39493
rooms
room1: true
rooms
room1
timestampOfLastPost: 39403942
users
039403fsjlkj: true
;alkjsdksdkj: true
I'm running a query on my user's rooms and grabbing the snapshot.key to get in this example "room1". Then inside of the first query I run another query and I use that key as a child reference to my rooms node to go to that room (rooms-->room1), and set is as my class object's title. From the 2nd query I'm pulling out the timestamp and use that for my object's timestamp.
I'm a bit confused how to get the childcount of out users.
I was running yet another nested query inside of my second query. But then I was getting all sorts of weird duplicate posts being added to my tableview when I was loading them. And that just doesn't seem very efficient anyways.
Is there a way in my second query to get both the timestamp and the children.count of my users node? The only way I've seen to do that is to run an observer on the specific node and do a snapshot.childrenCount.
edit:
currentUserFirebaseReference.child("rooms").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
self.interestsArray = []
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
let eachInterest = Interest()
let interestKey = snap.key
let title = snap.key.uppercaseString
eachInterest.title = title
//grabbing the user's current rooms and getting the snap.key to use for my next query.
DataService.ds.REF_INTERESTS.child(interestKey).observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
if let lastuser = snapshot.value!["lastJoin"] as? Int {
eachInterest.latestUser = lastuser
} else {
print("couln't pull that value")
}
//getting the timestamp
DataService.ds.REF_INTERESTS.child(interestKey).child("users").observeEventType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
// I can get the users children.count by doing yet another query, but my
// interestsArray gets all messed up and I get duplicates / weird
// loading in my tableview. So I'm looking to not do this whole
// separate query and just grab it from up above where I get the
// timestamp?
let snapshotChildren = String(Int(snapshot.childrenCount))
eachInterest.users = snapshotChildren
self.interestsArray.append(eachInterest)
//self.interestsArray.sortInPlace({ $0.latestUser < $1.latestUser })
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
})
})
}
}
}
}

Resources