Firebase Add New Key/Value to Pre-existing database without crashing xcode - swift

` let query = ref?.child("Reviews").queryOrdered(byChild: "UserID").queryEqual(toValue: myUser.userId)
query?.observeSingleEvent(of: .value) { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let uid = dict["UserID"] as! String
let review = dict["Body"] as! String
let rating = dict["Rating"] as! String
let titleID = dict["TitleID"] as! String
let reviewID = dict["ReviewID"] as! String
let ratingID = dict["RatingID"] as! String
`
THE ERROR OCCURS AT THE ratingID call to the database. It unwraps nil.
I am trying to adapt a pre existing Firebase database with a new key/value.
I then try to display entries in my tableview and I get a crash with unwrap returning nil. I know why this is happening and it's because the previous data does not have the new key/value I want to include in the node going forward. I have tried many different things such as if let and guard let without much fortune. How do I add new key/Values and still have the tableview read entries that don't have the new value?
I include an image of the current node and I want to add a 'RatingsID' to the node. When I do, I get the unwrap nil error.
Database node prior to new key/value

Your code is super close, just need to protect the code in case the ratingID key doesn't exist.
So change
let ratingID = dict["RatingID"] as! String
to
let ratingID = dict["RatingID"] as! String ?? ""
So if the RatingID node does not exist, you'll set ratingID to an empty string (or whatever string you want)
You could also code it to only operate on the value of that key if the node exists (not nil)
if let someVal = dict["xxx"] {
//do something with someVal
} else {
print("xxx node wasn't found")
}
Here's a complete example: We are reading some messages from the messages node and some of them have a test_key node and some dont. For those that don't, default string is assigned to test
let postsRef = self.ref.child("messages")
postsRef.observe(.childAdded) { snapshot in
let dict = snapshot.value as! [String: Any]
let msg = dict["msg"] as! String
let test = dict["test_key"] ?? "default string"
print(msg, test)
}

Related

how to load Data from multiple child and show in same tableView

Right now my tableview shows some data from a child in Firebase. Depending on which id is selected it only shows the data or comments regarding to this id.
I want to also show in the same tableView data from other id. How can I do that?
With this I get the data from a child with specific place ID.
func datenBankAbfrage () {
let placeIdFromSearch = ViewController.placeidUebertragen
ref = Database.database().reference().child("placeID/\(placeIdFromSearch)")
ref.queryOrdered(byChild: "userTime").queryLimited(toLast: 10).observe(DataEventType.value, with: {(snapshot) in
if snapshot.childrenCount > 0 {
self.table.removeAll()
for video in snapshot.children.allObjects as! [DataSnapshot] {
let Object = video.value as? [String: AnyObject]
let userName = Object?["userName"]
let userGroup = Object?["userGroup"]
let userComment = Object?["userComment"]
let userTime = Object?["userTime"]
let userLikes = Object?["userLikes"]
let commentId = Object?["commentId"]
ViewComments.commentIDNew = commentId as! String
let video = importComment(userName: userName as! String, userGroup: userGroup as! String, userComment: userComment as! String, userTime: userTime as! Int, userLikes: userLikes as! Int, commentId: commentId as! String)
self.table.insert(video, at: 0)
self.tableView.reloadData()
}
}
})
}
So I am searching somethings like
ref = Database.database().reference().child("placeID/\(placeIdFromSearch) & this child and that child ")
The only way to get a subset of the child nodes of a path is with a query, so if the nodes either share a common property (queryEqualToValue(...)) or if they are within a contiguous range (queryStartingAtValue/queryEndingAtValue).
Beyond that it is not possible to load a subset of child nodes with an OR like condition. This means that you will have to load each branch with a separate read operation, and then merge the data together in your application code. This is not nearly as slow as you may expect, since Firebase pipelines the requests together over the same connection.

Firebase get Map Object data

Im trying to retrieve an address I have stored in a Map object within Firebase.
But I can't seem to retrieve any data, When I print out the result it doesn't display anything.
Here is my Firebase Structure
This is the request I'm making to Firebase
let key = UserDefaults.standard.value(forKey: "uid") as? String ?? "Null"
let docRef = firebaseDB.collection("user").document(key)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let docData = document.data()
let city = docData!["city"] as! String
let country = docData!["country"] as! String
let county = docData!["county"] as! String
let lineOne = docData!["lineOne"] as! String
let lineTwo = docData!["lineTwo"] as! String
let postCode = docData!["postCode"] as! String
print(city,country,county,lineOne,lineTwo,postCode)
} else {
print("Document does not exist")
}
If this is crashing
let city = docData!["city"] as! String
it indicates a node does not contain a child of 'city'. Perhaps it's missing entirely or is City instead of city.
Force unwrapping a var (e.g. as!) which could potentially be nil is essentially stating 'Hey var, you will never be nil' and can lead to app crashes and instability.
A better way to handle this and protect your code is to properly handle data in the cases where it may not be what is expected.
A better method is to use nil coalescing like this, which provides a default value if city is nil
let city = data!["city"] as? String ?? "no city selected"
or if you want to work with nil then change the way you're reading the child nodes to
let city = document.get("city")
Keeping in mind that while this code won't crash, city could be nil in this situation so you need to handle that accordingly in the code following that statement.
You can also use if let statements
var aPlace = PlaceStruct()
if let city = document.get("city") as? String {
aPlace.city = city
} else {
aPlace.city = "Hometown"
}
or guard statements to protect your data.
Whatever the situation, don't force unwrap potentially nil vars unless you know they will never be nil - handle those vars that can be nil gracefully.

Getting values from Firebase snapshot in Swift

Im successfully getting data from Firebase but I can't manage to push it into array to use. My database is as follows:
users
-Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
-email : "mike#gmail.com"
-lists
-LJiezOzfDrqmd-hnoH-
-owner: Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
-LJif-UgPgbdGSHYgjY6
-owner: Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2
shopping-lists
-LJh6sdBJtBCM7DwxPRy
-name: "weekly shopping"
-owner: "mike#gmail.com"
I have a home page after login that shows existing shopping lists on table if they exist. On viewDidLoad() I get shopping list IDs from the user and use those IDs as a reference to get details from shopping-lists.
However, I cant manage to save these data into an array as it gets deleted after closure. How can I do that in a clean way?
override func viewDidLoad() {
super.viewDidLoad()
SVProgressHUD.show()
tableView.allowsMultipleSelectionDuringEditing = false
// Sets user variable - must have
Auth.auth().addStateDidChangeListener { auth, user in
guard let user = user else { return }
self.user = User(authData: user)
// If new user, write into Firebase
self.usersRef.observeSingleEvent(of: .value, with: { snapshot in
if !snapshot.hasChild(self.user.uid) {
self.usersRef.child(user.uid).setValue(["email": user.email!])
}
})
// Get shopping lists data from "users/lists"
self.usersRef.child(user.uid).child("lists").observe(.value, with: { snapshot in
// Get list IDs
if snapshot.exists() {
if let result = snapshot.children.allObjects as? [DataSnapshot] {
for child in result {
self.listNames.append(child.key)
}
}
}
// Use list IDs - to get details
for item in self.listNames {
let itemRef = self.shoppingListsRef.child(item)
itemRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
let name = value["name"] as? String ?? ""
let owner = value["owner"] as? String ?? ""
let shoppingList = ShoppingList(name: name, owner: owner)
self.items.append(shoppingList)
}
})
}
})
self.tableView.reloadData()
SVProgressHUD.dismiss()
}
}
(the question is a bit unclear so several parts to this answer to cover all possibilities. This is Swift 4, Firebase 4/5)
You don't really need to query here since you know which nodes you want by their key and they will always be read in the in order of your listNames array. This assumes self.listNames are the keys you want to read in.
for item in listNames {
let itemRef = shoppingListsRef.child(item)
itemRef.observe(.value, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
let name = value["name"] as? String ?? ""
let owner = value["owner"] as? String ?? ""
print(name, owner)
}
})
}
Generally, queries are used when you are searching for something within a node - for example if you were looking for the node that contained a child name of 'weekly shopping'. Other than that, stick with just reading the nodes directly as it's faster and has less overhead. Keep reading...
I also removed the older NSDictionary and went with the Swift [String: Any] and modified your error checking
However, the real issue is reading that node with an .observe by .value. Remember that .value reads in all children of the node and then the children need to be iterated over to get each separate DataSnapshot. Also, .observe leaves an observer on the node notifying the app of changes, which I don't think you want. So this will answer the question as posted, (and needs better error checking)
for item in listNames {
let queryRef = shoppingListsRef
.queryOrdered(byChild: "name")
.queryEqual(toValue: item)
queryRef.observe(.value, with: { (snapshot) in
for child in snapshot.children { //even though there is only 1 child
let snap = child as! DataSnapshot
let dict = snap.value as! [String: Any]
let name = dict["name"] as? String ?? ""
let owner = dict["owner"] as? String ?? ""
print(name, owner)
}
})
}
And the answer...
This is probably more what you want...
for item in listNames {
let queryRef = shoppingListsRef
.queryOrdered(byChild: "name")
.queryEqual(toValue: item)
queryRef.observeSingleEvent(of: .childAdded, with: { snapshot in
let dict = snapshot.value as! [String: Any]
let name = dict["name"] as? String ?? ""
let owner = dict["owner"] as? String ?? ""
print(name, owner)
})
}
note the .childAdded instead of .value which presents the snapshot as a single DataSnapshot and doesn't need to be iterated over and the .observeSingleEvent which does not leave an observer attached to each node.
Edit
Based on additonal information, it would be best too change the structure to this
shopping-lists
-LJh6sdBJtBCM7DwxPRy
-name: "weekly shopping"
-uid: "Wc1EtcYzZSMPCtWZ8wRb8RzNXqg2"
and then when the user logs in just query the shopping lists node for any uid that's theirs.

Swift Firebase read children of a child

So I am trying to read the children of the autoID children beneath "Recipes" below is a picture of my Firebase database and beneath that is the method that is supposed to retrieve the value of "Description" and "Name" and insert them into variables.
The error that I am currently getting when running the app is this:
Could not cast value of type '__NSCFString' (0x10ad2afb8) to 'NSDictionary' (0x10ad2bfa8).
ref = Database.database().reference()
databaseHandle = ref?.child("Recipes").observe(.childAdded) { (snapshot) in
for snap in snapshot.children
{
let recipeSnap = snap as! DataSnapshot
let recipeID = recipeSnap.key
let dict = recipeSnap.value as! [String:AnyObject]
let recipeName = dict["Name"] as! String
let recipeDescription = dict["Description"] as! String
print("key = \(recipeID) and name = \(recipeName) and description = \(recipeDescription)")
}
}
The print statement is just there for testing.
Try the following and let me know if it works now:
// SEARCHES FOR SHARING CODE IN DATABASE (ONLINE)
let parentRef = Database.database().reference().child("Recipes")
parentRef.observeSingleEvent(of: .value, with: { snapshot in
// SHOWING WHATEVER WAS RECEIVED FROM THE SERVER JUST AS A CONFIRMATION. FEEL FREE TO DELETE THIS LINE.
print(snapshot)
// PROCESSES VALUES RECEIVED FROM SERVER
if ( snapshot.value is NSNull ) {
// DATA WAS NOT FOUND
print("– – – Data was not found – – –")
} else {
// DATA WAS FOUND
for user_child in (snapshot.children) {
let user_snap = user_child as! DataSnapshot
let dict = user_snap.value as! [String: String?]
// DEFINE VARIABLES FOR LABELS
let recipeName = dict["Name"] as? String
let recipeDescription = dict["Description"] as? String
print("– – – Data for the recipe \(recipeName) with the description \(recipeDescription) was found successfully! – – –")
}
}
}
If you only want to retrieve the name and description for one specific recipe, you should change the third line to
parentRef.queryEqual(toValue:DefineWhatToSearchForHere).observeSingleEvent(of: .value, with: { snapshot in
If you constantly want to update to reflect changes, you can either call this function every x seconds using a timer and adding it to override func viewDidLoad() such as
time = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(ViewController.updateFBData), userInfo: nil, repeats: true)
after creating a function called func updateFBData() in which you do whatever you want to do to get new data (see above) and calling it in a defined timeInterval
or you can do what Attila Hegedüs in this excellent tutorial.

Retrieve all values from child i collectionView

I´m trying to retrieve all the values from one child into a collectionView
I keep getting this error
Could not cast value of type '__NSDictionaryM' in this line
let follower = snapshotValue!["Followers"] as! String
here is my code
followRef.child("users").child((FIRAuth.auth()?.currentUser?.displayName)!).observe(FIRDataEventType.value, with: {
snapshot in
let snapshotValue = snapshot.value as? Dictionary<String,AnyObject>
let follower = snapshotValue!["Followers"] as! String
self.postsFollow.insert(postFollowStruct(follower: follower) , at: 0)
self.followersView.reloadData()
})
By what i understand you have many json objects in your snapshot.value? Then you want to cast to an object each one of them and put them into an array?
if let dict = snapshot.value as? [String: AnyObject]{
let followers = dict
for follower in followers {
if let restDict = follower.value as? [String:AnyObject] {
let followerObj = Follower()
followerObj.setValuesForKeys(restDict)
//add to array
}
}
}