Reading child value from firebase database in swift - swift

I am trying to read a child value so I can add 1 to it then update the value, but my code to read the value is giving me a SIGABRT error and crashing, what is wrong with it? REF_FEEDMESSAGES is a reference to the database that holds all the messages
var stringLikes = REF_FEEDMESSAGES.child(key).value(forKey: "likes") as! String ?? "0"

You need ( observe / observeSingleEvent is up to you according to the logic of your app )
REF_FEEDMESSAGES.child("\(key)/likes").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
}) { (error) in
print(error.localizedDescription)
}
Using this .value(forKey: "likes") as! String for sure will crash the app , as it's not a local dictionary to query it's data synchronously

Related

Firebase removeObserver is not working in Swift 5

I am using Firebase's Realtime database in my app. I am fetching data from the database and do some change and after that I am removing the observer which is not working fine.
I have some data in Realtime Database like this:
I am using firebase's observe(.value) function to get this value and after that I am updating an entry and then I am removing the observer. This is my code:
func updatePoints() {
let firebaseId = UserDefaults.standard.value(forKey: "firebaseId") as? String ?? ""
let reference = self.database.child("Points").child(firebaseId)
var handler : UInt = 0
handler = reference.observe(.value, with: { snapshot in
guard let userPoints = snapshot.value as? [String : Any] else {
print("no points data found")
return
}
let pointsLeft = userPoints["points_left"] as? Int ?? 0
reference.child("points_left").setValue(pointsLeft - 1)
reference.removeObserver(withHandle: handler)
})
}
The problem now is, this observer runs twice. For example, if "points_left" : 10, then after this function the points left will have 8 value but it should have 9 instead. It is running twice and I am not understanding why is it doing so as I am using removeObserver. Can someone help me with this?
The reason to the above unexpected behaviour is the setValue function you called to update the points is triggering another .value event in the database. Then it triggers the observer again. Therefore, by the time you remove the observer, it has already triggered twice. This leads to decrease of points by 2 instead of 1.
So if u interchange the last two lines, by the time you call the setValue function observer is removed. So it will not get triggered for the second time.

Delete document from cloud firestore

I am trying to implement a function that allows the user to delete chats that they have with other users.
At the moment the code works and the firestore document is deleted successfully however as soon as the delete happens the code crashes and i get this error "Fatal error: Unexpectedly found nil while unwrapping an Optional value" next to the id = data!["id"] in the code. I guess that after the delete happens firestore keeps listening for documents and finds an empty collection following the delete. Does anyone know how to stop this from happening?
public func deleteConversation(conversationId: String, completion: #escaping (Bool) -> Void) {
// Get all conversations for current user
let CurrentUser = Auth.auth().currentUser?.uid
let db = Firestore.firestore()
let ConversationRef = db.collection("users").document(CurrentUser!).collection("conversations").document(test!)
print("deleting conversation with \(test!)")
ConversationRef.addSnapshotListener { snapshot, error in
guard let document = snapshot else {
print("Error getting documents: \(String(describing: error))")
return
}
let data = document.data()
if let id = data!["id"] as? String, id == conversationId {
print("conversation found")
ConversationRef.delete()
}
completion(true)
print("deleted conversation")
}
}
The problem comes from:
ConversationRef.addSnapshotListener { snapshot, error in
By calling addSnapshotListener you're adding a listener that:
Gets the document snapshot straight away,
Continues listening for changes and calls your code again then there are any.
The problem is in #2, as it means your code executes again when the document is deleted, and at that point document.data() will be nil.
The simplest fix is to only read the document once:
ConversationRef.getDocument { (document, error) in

Not receiving error information from read data in firebase using swift

I have managed to get firebase working and to read data from the database and present it.
I'm taking pictures and getting CoreML to work out what the item is then sending it to the database to return data on the item.
If the item is not in the database I, therefore want this to error but instead, the return is just blank. It seems like the firebase error block isn't working at all as it doesn't get to if after executing the first part of the code.
I have tried using a do catch block also but with no luck.
Please see the code attached:
ref.child("items").child("\(self.final)").observeSingleEvent(of: .value, with: { (snapshot) in
// Get item value
let value = snapshot.value as? String ?? ""
print(value)
self.calorieCount.text = "\(value)"
}) { (error) in
print(error.localizedDescription)
print("error")
self.calorieCount.text = "Item not found, you will be able to add this soon"
}
}
Would somebody be able to tell me why the error part doesn't work when the item isn't in the database?
Thanks in advance!
Not having data at a location is not considered an error in the Firebase API, so the error closure doesn't get called. Instead your regular closure is called with an empty DataSnapshot, for which you can test with:
ref.child("items").child("\(self.final)").observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() {
self.calorieCount.text = "Item not found, you will be able to add this soon"
}
else {
let value = snapshot.value as? String ?? ""
self.calorieCount.text = "\(value)"
}
})

Unable to access variable inside closure swift

I'm trying to get my firebase database data into a variable to use it in my project
var someArray = [Array]()
let dbRef = Database.database().reference().child("SomeDatabase")
func loadSomeDatabaseData {
dbRef.observeSingleEvent(of: .value) { (snapshot) in
let someDict = snapshot.value as! [String:Any]
let keysOfSomeDict = Array(someDict.keys)
self.someArray.append(contentsOf: keysOfSomeDict)
self.collectionView?.reloadData()
}
}
I've tried calling loadSomeDatabaseData() in my viewDidload, followed by printing someArray, which results in an empty array. I know the keysOfSomeDict array has the correct data that I want, since i tried printing this array directly inside the closure. I would however also like to be able to print and use this data elsewhere in my app.
The Firebase observeSingleEvent method is asynchronous method. it executive in background only because it take time to fetch data from Firebase.
if you print array immediately means you get only empty array.
so print array once you get the data from Firebase. for that you can use escaping closure
Function Declaration:
func loadSomeDatabaseData(resultArray : #escaping([Array])->()) {
dbRef.observeSingleEvent(of: .value) { (snapshot) in
let someDict = snapshot.value as! [String:Any]
let keysOfSomeDict = Array(someDict.keys)
self.someArray.append(contentsOf: keysOfSomeDict)
resultArray(self.someArray)
}
}
Func Call:
self.loadSomeDatabaseData{(firebaseReposne) in
print("FirebaseData" , firebaseReposne) // Hope here you will get your firebase data.
self.collectionView?.reloadData()
}
The observeSingleEvent is asynchronous. So immediately printing someArray after calling loadSomeDatabaseData will result in an empty array. It takes sometime to retrieve the data from Firebase api.
To use this data elsewhere in the app, you could set a flag indicating the data is loaded or send a notification to inform the data is available.

When retrieving data from Firebase Database, <null> is returned: "Snap (...) <null>"

I'm a relatively new Swift programmer and am using Firebase for the first time so please excuse any misunderstandings I may have and my lack of knowledge about terminology.
I am attempting to retrieve data about a user that is stored in a database (email and username).
The code successfully finds the userID in the database. The userID is then used in order to navigate into the directory containing the username and email. It stores those values in snapshot.
For some reason, when snapshot is printed, it shows the userID but the contents of the directory (username and password) are shown as <null>. I am certain that the directory I am attempting to access and retrieve data from exists and is not empty (it contains a username and email). I wantsnapshot to store the username and email, but printing shows that it is not doing so correctly and I cannot figure out why.
here is my code block:
func checkIfUserIsLoggedIn() {
if Auth.auth().currentUser?.uid == nil {
perform(#selector(handleLogout), with: nil, afterDelay: 0)
} else {
let uid = Auth.auth().currentUser?.uid;
Database.database().reference().child("Users").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot)
if let dictionary = snapshot.value as?[String:AnyObject] {
self.userLabel.text = dictionary["name"] as? String
}
}, withCancel: nil)
}
}
and here is what is being printed to the console:
Snap (ywU56lTAUhRpl3csQGI8W8WmQRf1) <null>
Here is the database entry I am attempting to reach and log to snapshot:
I'm a new Stack Overflow user and don't have enough experience on the site to be allowed to embed images in posts, so this is the external link
Thanks for reading, any help would be much appreciated!!
Your reference in Firebase is to "users", but you are using .child("Users") in your code. Make sure your lookup matches case to your node. I find it best to create a reference to that node and use it for writing to and reading from.
let usersRef = Database.Database().reference().child("users")
Snap (ywU56lTAUhRpl3csQGI8W8WmQRf1) <null> the portion in parenthesis refers to the end node of what you are trying to observe. In this case it refers to uid!.
if u want to get username or email then you make first the model class for
Example:-
class User: NSObject {
var name: String?
var email: String?
}
then user firebase methed observeSingleEvent
FIRDatabase.database().reference().child("user").child(uid).observeSingleEvent(of: .value, with: { (snapShot) in
if let dictionary = snapShot.value as? [String: Any]{
// self.navigationItem.title = dictionary["name"] as? String
let user = User()
user.setValuesForKeys(dictionary)
self.setUpNavigationBarWithUser(user: user)
}
})`
if it is not finding your asking values, you are asking wrong directory. check firebase db child name it must be exactly like in your code ("Users")