Reputation: 195
I've got a code which normally should return to me a value from Firebase.
My Firebase struct is :
Experience{
UserId{
LDG_DAY: "4"
LDG_NIGHT: "0"
APCH_IFR: "0"
}
}
My code is :
func getUserExp(){
let ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let Date = self.flightDate.text
ref.child("Experience")/*.child(userID!)*/.observeSingleEvent(of: .value) {(snapshot) in
if snapshot.hasChild(userID!){
let value = snapshot.value as? NSDictionary
let ldg_day = value?["LDG_DAY"] as? String ?? "123"
let ldg_night = value?["LDG_NIGHT"] as? String ?? "0"
let apch_ifr = value?["APCH_IFR"] as? String ?? "0"
self.intLdgDay = Int(ldg_day)!
self.intLdgNight = Int(ldg_night)!
self.intApchIfr = Int(apch_ifr)!
print("string = \(ldg_day) int = \(self.intLdgDay)")
}
}
}
Now the code didn't work as I would like... In fact my code return the basic as? String ?? "123"
value but the snapshot.value get the good value from firebase ...
What's wrong ? I use this code for many other part of my app and no problems about it ?
Thanks for your help
Upvotes: 1
Views: 67
Reputation: 11539
Assuming your struct is from the root, and Experience
contains more than one user ID, your code is currently observing the value for all user IDs since the /*.child(userID!)*/
is commented out.
Therefore you are requesting every user's experience and checking on the client if the current user exists as a child – this will succeed if the current user's ID is present at Experience/$uid
.
ref.child("Experience")/*.child(userID!)*/.observeSingleEvent(of: .value) { (snapshot) in
if snapshot.hasChild(userID!) {
let value = snapshot.value as? NSDictionary
Now we have a snapshot with all Experiences and we've confirmed that it has a child for the current user's ID – we would need to get that child and cast the value of that to a dictionary.
let value = snapshot.childSnapshot(forPath: userID).value as? NSDictionary
This fixes the issue but obviously, we don't want to download every experience on a single user's device, and they maybe shouldn't even have the permission to request that reference location either.
So if you uncomment .child(userID!)
, the snapshot will be of just one Experience, so snapshot.hasChild(userID!)
will fail. Instead, you can use snapshot.exists()
and/or a conditional cast to determine if the snapshot for the userID
is existent and/or thereby castable.
func getUserExp() {
let ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let Date = self.flightDate.text
ref.child("Experience").child(userID!).observeSingleEvent(of: .value) { snapshot in
if snapshot.exists() {
let value = snapshot.value as? [String:String]
let ldg_day = value?["LDG_DAY"] ?? "123"
let ldg_night = value?["LDG_NIGHT"] ?? "0"
let apch_ifr = value?["APCH_IFR"] ?? "0"
self?.intLdgDay = Int(ldg_day)!
self?.intLdgNight = Int(ldg_night)!
self?.intApchIfr = Int(apch_ifr)!
print("string = \(ldg_day) int = \(self.intLdgDay)")
} else {
print("experience for \(snapshot.key) doesn't exist")
}
}
}
You can clean this up a bit with a struct and extension.
// Experience.swift
struct Experience {
var ldg_day: String
var ldg_night: String
var apch_ifr: String
}
extension Experience {
static var currentUserRef: DatabaseReference? {
return Auth.auth().currentUser.flatMap {
return Database.database().reference(withPath: "Experience/\($0.uid)")
}
}
init?(snapshot: DataSnapshot) {
guard snapshot.exists() else { return nil }
let value = snapshot.value as? [String:String]
self.ldg_day = value?["LDG_DAY"] ?? "123"
self.ldg_night = value?["LDG_NIGHT"] ?? "0"
self.apch_ifr = value?["APCH_IFR"] ?? "0"
}
}
Et voilà,
func getUserExp() {
Experience.currentUserRef?.observeSingleEvent(of: .value, with: { [weak self] in
if let experience = Experience(snapshot: $0) {
self?.intLdgDay = Int(experience.ldg_day)!
self?.intLdgNight = Int(experience.ldg_night)!
self?.intApchIfr = Int(experience.apch_ifr)!
print("string = \(experience.ldg_day) int = \(self.intLdgDay)")
} else {
print("experience for \($0.key) doesn't exist")
}
})
}
Upvotes: 0
Reputation: 35677
I believe you want to ensure the node exists before trying to read the child data.
NOTE:
I see the path to read has the uid commented out so it's unclear if you intended to read a single user (leaving in the uid) or if you actually wanted to load every user at one time (thousands). This answer assumes you are intending to read that specific user node only. See @Callam answer if you intended to read ALL of the users nodes at one time.
The code you have now is using snapshot.hasChild which looks within the node to see if the child, the users uid exists, and it doesn't so the code will always fail.
if snapshot.hasChild(userID!)
I think what you want to do is use snapshot.exists to ensure it's a valid node before reading. Here's the code:
let experienceRef = self.ref.child("Experience")
let usersExpRef = experienceRef.child(uid)
usersExpRef.observeSingleEvent(of: .value) { snapshot in
if snapshot.exists() {
let value = snapshot.value as! [String: Any]
let ldg_day = value["LDG_DAY"] as? String ?? "123"
print("string = \(ldg_day)")
} else {
print("the \(uid) node does not exist")
}
}
I would also suggest safely unwrapping options before attempting to work with them as they could be nil, and that would crash your code.
guard let thisUser = Auth.auth().currentUser else { return }
let uid = thisUser.uid
Note I also replaced the old objc NSDictionary with it's Swifty counterpart [String: Any]
Upvotes: 1