Not receiving error information from read data in firebase using swift - 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)"
}
})

Related

How to grab data from Firebase Realtime Database and return it in a function in Swift

i am currently working on a project where users have accounts with information about them. And I want that every client can get and load the data with just the ID of the target. I wanted to solve this with a function, but i don't know how to return the data, I hope you can help me!
Screenshot Firebase
func getUserData(targetID: String)->String{
let db = Database.database(url: "link")
db
.reference()
.child("users")
.child(targetID)
.child("/username")
.getData(completion: { error, snapshot in
guard error == nil else {
print(error!.localizedDescription)
return;
}
let userName = snapshot.value as? String ?? "null";
});
return userName //won't work, but how can I make it work?
}

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

Reading child value from firebase database in 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

How to retrieve the value of the key changed in a child in firebase realtime database using observe

i was trying to write a code to pull a data from the realtime database using observe for child changed. But i don't know what the mistake is but the code fails. From my debugging study, i found that the observe was successfully triggered when the value changed, but it fails when i try to get the snapshot value to a variable. How should data be retrieved in a observe for child change case.
func userBalance() {
DatabaseProvider.Instance.userRef.child(UserDataHandler.Instance.user_id).observe(FIRDataEventType.childChanged, with: { (snapshot) in
print("Snapshot: ", snapshot)
print("Snapshot Value", snapshot.value)
guard let data = snapshot.value as? NSDictionary else {
print("checkpoint fail test")
return
}
guard let userBalance = data[Constants.BALANCE] as? String else { return }
// update UserDefaults
userDefault.setString(userBalance, forKey: "userBalance")
//update local session
UserDataHandler.Instance.balance = userBalance
}) }
kindly help me out, thanks in advance.
Debugging Data :
Snapshot: Snap (balance) 100
Snapshot Value Optional(100)
checkpoint fail test
Thanks guys, i found the answer, may be it will help someone else. I am put down the new code that worked.
DatabaseProvider.Instance.userRef.child(UserDataHandler.Instance.user_id).observe(FIRDataEventType.childChanged, with: { (snapshot) in
guard let key = snapshot.key as? String else { return }
guard let value = snapshot.value as? String else { return }
if key == Constants.BALANCE {
guard let userBalance = value as? String else { return }
// update UserDefaults
userDefault.setString(userBalance, forKey: "userBalance")
//update local session
UserDataHandler.Instance.balance = userBalance
}
}) }
The problem was that the observe detects the change in the database as one by one, so the snapshot comes as a single data for every change, if you change multiple values in a go, the observe detects it as multiple changes, one by one with each change as change of one value. So when i changed it to direct string it worked. You were right #Achref Gassoumi the problem was with the cast. Just after you told about it, i tried it out. Thanks.
I think your problem probably is with the cast
try the following :
DatabaseProvider.Instance.userRef.child(UserDataHandler.Instance.user_id).observe(FIRDataEventType.childChanged, with: { (snapshot) in
guard let data = snapshot.value as? [<String,anyObject>] else {
print("checkpoint fail test")
return
}
print (data)

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