updateChildValues() does not work, but setValue() does - swift

I'm trying to implement fan out and here is the problem: When I'm trying to updateChildValues in my usersOrdersRef reference, it just does nothing. I expect it to create the user_orders node, and child node with my userid and put a value there.
If I use setValues instead, it works fine, but I need to add values without deleting all previous data. What am I doing wrong?
Code:
for item in orderArray {
let childRef = ref.child(item.date!)
//this works fine
childRef.updateChildValues(item.convertToDictionary() as! [AnyHashable : Any]) { [unowned self] (error, reference) in
if error != nil {
print(String(describing: error?.localizedDescription))
}
let usersOrdersRef = Database.database().reference().child("users-orders").child(self.appUser.uid)
var primary = false
if (item.precursor == "") { primary = true }
//this does not work, but if i use "setValue" instead it will store data
usersOrdersRef.updateChildValues([item.date : primary])
}
}

Your problem:
//this does not work, but if i use "setValue" instead it will store data
usersOrdersRef.updateChildValues([item.date : primary])
I found myself in the same situation.
The problem was the data type of the key. When I casted the key to a String it started to function.
Hope this helps someone in the same situation.

I have the same problem
If I use
let newHashTagRef = Api.HashTag.REF_HASHTAG.child(word.lowercased())
newHashTagRef.updateChildValues([newPostId: true])
=> not working
when I replace
let newHashTagRef = Api.HashTag.REF_HASHTAG.child(word.lowercased())
newHashTagRef.setValues([newPostId: true])
=> it works but it will delete all previous data
Solution
let newHashTagRef =
Api.HashTag.REF_HASHTAG.child(word.lowercased()).child(newPostId)
newHashTagRef.setValue(true)

I resolve it by myself , now it works like this
let usersOrdersRef = Database.database().reference().child("users-orders").child(self.appUser.uid).child(item.date!)
var primary = false
if (item.precursor == "") { primary = true }
usersOrdersRef.setValue(primary)
In that case setValue does not delete all previous data, just adds new

You just need to cast your optional data key into the updateChildValues function.
Updated Code:
for item in orderArray {
let childRef = ref.child(item.date!)
//this works fine
childRef.updateChildValues(item.convertToDictionary() as! [AnyHashable : Any]) { [unowned self] (error, reference) in
if error != nil {
print(String(describing: error?.localizedDescription))
}
let usersOrdersRef = Database.database().reference().child("users-orders").child(self.appUser.uid)
var primary = false
if (item.precursor == "") { primary = true }
// this will work now, as we have explicity cast the key
usersOrdersRef.updateChildValues([item.date! : primary])
}
}

Related

How can I add these Firestore fields to a Dictionary?

I am looking to add all my "usernames" into a dictionary. I am having some trouble doing this. I am sure it's very obvious, but I am very new to coding.
I am stuck at, right now and can't seem to find a clear answer anywhere:
func fetchUser() {
let db = Firestore.firestore()
let usernameSearch = db.collection("users")
usernameSearch.getDocuments { (snapshot, error) in
if error != nil {
print("Error obtaining usernames")
} else {
for field in snapshot!.documents {
let field = field.get("username")
print(field!)
}
}
}
}
I would really appreciate it if somebody could help me out. I am sure it's very obvious, or I'm just doing it totally wrong.
First, get into the habit of safely unwrapping over force unwrapping. And choose more accurate names for your objects (i.e. usersCollection over usernameSearch). However, in this case, there's no need to instantiate individual properties for the database and the collection since they're not being used anywhere else but here (so be efficient and omit them).
var usersDictionary = [String: [String]]()
func fetchUser() {
Firestore.firestore().collection("users").getDocuments { (snapshot, error) in
if let snapshot = snapshot { // unwrap the snapshot safely
var usernames = [String]()
for doc in snapshot.documents {
if let username = doc.get("username") as? String {
usernames.append(username)
}
}
usersDictionary["usernames"] = usernames
} else {
if let error = error {
print(error)
}
}
}
}
Or if you actually meant an array of users:
var usersArray = [String]()
func fetchUser() {
Firestore.firestore().collection("users").getDocuments { (snapshot, error) in
if let snapshot = snapshot { // don't force unwrap with !
for doc in snapshot.documents {
if let username = doc.get("username") as? String {
usersArray.append(username)
}
}
} else {
if let error = error {
print(error)
}
}
}
}
I'm assuming that what you're looking for is an Array, not a Dictionary. I'll also assume that you are indeed getting the correct value that you'd expect out of field.get("username"), e.g. a string such as "Bob." Therefore, what you are trying to do is map the list of document objects to a list of strings.
If you scroll to the Topics section of the Array documentation from Apple, you can find some of the operations they provide for arrays such as snapshot!.documents.
One of those operations is actually map, and its description is:
Returns an array containing the results of mapping the given closure over the sequence’s elements.
https://developer.apple.com/documentation/swift/array/3017522-map
In other words, you provide a transformation to perform for each instance of a document belonging to the snapshot!.documents Array and get back a new Array containing the resultant values of that transformation.
In this case I will use a more specific operation; compactMap. We have to try and cast the returned value from Any to String. If that does not succeed, it will return nil, and we'll want to filter that out. I expect it to be an unlikely case due to the type requirements made by the Firebase Console, but it's good to be aware of it. Here is the example:
func fetchUsernames(from usernameCollection: String, completion: #escaping ([String]) -> Void) {
let db = Firestore.firestore()
let collection = db.collection(usernameCollection)
collection.getDocuments { snapshot, error in
guard error != nil,
let usernames = snapshot?.documents.compactMap { $0.get("username") as? String }
else { return print("Error obtaining usernames") }
completion(usernames)
}
}
The key line here being let usernames = snapshot?.documents.compactMap { $0.get("username") }. We are passing the map function a closure. This closure is passed an argument itself; each value from the snapshot?.documents array. You may refer to this passed in value with $0.

Swift - Firebase Error, How to Access to First Child

I'm using firebase now and I got stuck with this.
I have a contactList like below.
I want to get the keys (like "-L8per5UdCjQyCBxs5Oo" for the one) but I don't know how to do this.
I tried something like this:
guard let contactList = self.user?.contactList else { return }
print(contactList[0])
And the error message is this.
Cannot subscript a value of type '[String : [String : String]]' with an index of type 'Int'
How can I fix this problem?
Thank you.
var ref:DatabaseReference?
var contactListArray:[DataSnapshot] = [] //you could create an array to save your children
func startListening() {
ref = Database.database().reference().child("contactList")
ref?.observe(.childAdded) {(snapshot) in
self.contactListArray.append(snapshot) // doing this, you can access the first child by contactListArray[0]
// This is how to access info in your snapshot if you need it
let key = snapshot.key //HERE: you'll get the keys
let props = snapshot.value as! Dictionary<String, AnyObject>
let userId = props["userId"] //HERE: how to access to data
}
}
You could iterate through fetched objects via:
for (key, value) in contactList {
///
}
If you want to get the first object, you can write:
contactList.first

What would be the most efficient way to check equality of a Firebase live database node value and also change it?

Above is what I have in my live database in Firebase. What I am trying to do is check if the current user's partyStatus is equal to the bool of false or true. And if it is false I want to change it to true.
This is what I tried so far:
usersReference.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild(uid) {
//continue another observeSingleEvent for the next value which is "partyStatus" then check the value then change it
}
How can I do this more efficiently with Firebase and Swift?
Here's some sample code that could be used.
What we are doing here is examining the specific node in question for user_0, the partyStatus node. If it exists, get it's current value (assuming it's a bool) then toggle it (false to true or true to false) and if it doesn't exist, create it with a default value of false.
let ref = self.ref.child("users").child("user_0").child("partyStatus")
ref.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
let isPartying = snapshot.value as! Bool
ref.setValue(!isPartying)
} else {
ref.setValue(false) //it doesn't exist to set to default of false
}
})
//note that self.ref is a class var ref to my Firebase.
You current code load the entire usersReference to then check if a single child exists in it.
A more efficient way to do the same check is to only load the reference for that user and then check if the snapshot exists:
usersReference.child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
if snapshot.childSnapshot(forPath: "partyStatus") as! Bool == false {
usersReference.child(uid).child("partyStatus").setValue(true)
}
}
Did you try runTransactionBlock , Simply take the value , test , and return new value :
usersReference.child(uid).child("partyStatus").runTransactionBlock { (data) -> TransactionResult in
let result = data
// data = partyStatus value
if var value = data.value as? Bool {
if value == false { // if value is false will change value to true in database
value = true
result.value = value
} // if not that mean the value is true and will return true
}
return TransactionResult.success(withValue: result)
}
And for more information about runTransaction Firebase Doc .

True becomes false using SwiftyJson in swift

I am getting the opposite value of a boolean when using .boolValue
I have this code.
let _ = channel.bind(eventName: "like-unlike-cake", callback:
{(data:Any?)->Void in
let likedCake = JSON(data!)["like"]
print("liked cake: \(likedCake)")
print("Boolean: \(likedCake["liked_by_current_user"].boolValue)")
liked cake: {
"liked_by_current_user" : true
}
}
Boolean: false
But when I use .bool it gives me nil. Does someone help me on this.
UPDATE This would be the json I want to fetch
{
"like": {
"id": "66642122-7737-4eac-94d2-09c9a35cbef8",
"liked_by_current_user": true
}
}
Try the following:
let _ = channel.bind(eventName: "like-unlike-cake", callback {
(data:Any?)->Void in
if let jsonData = data {
let json = JSON(jsonData)
let isLikedByCurrentUser = json["like"]["liked_by_current_user"].boolValue
print(isLikedByCurrentUser)
} else {
// your data is nil
debugPrint("data error")
}
}
also you can try to cast your data: Any into Data by replacing the line:
if let jsonData = data as? Data {
let json = JSON(data: jsonData)...
cause sometimes SwiftyJSON have problems with creating JSON objects from Any.

How to delete from Firebase database

Hi i have followed this tutorial: https://www.youtube.com/watch?v=XIQsQ2injLo
This explains how to save and retrieve from the database, but not how to delete. I am wondering how to delete the database node that belongs to the cell that is being deleted. Thanks
Edit. Code updated for Swift 3 and Swift 4.
I'm always using the remove with completion handler:
static let ref = Database.database().reference()
static func remove(child: String) {
let ref = self.ref.child(child)
ref.removeValue { error, _ in
print(error)
}
}
So for example if I want to delete the following value:
I call my function: remove(child: "mixcloudLinks")
If I want to go deeper and delete for example "added":
I need to change the function a little bit.
static func remove(parentA: String, parentB: String, child: String) {
self.ref.child("mixcloudLinks").child(parentA).child(parentB).child(child)
ref.removeValue { error, _ in
print(error)
}
}
Called like:
let parentA = "DDD30E1E-8478-AA4E-FF79-1A2371B70700"
let parentB = "-KSCRJGNPZrTYpjpZIRC"
let child = "added"
remove(parentA: parentA, parentB: parentB, child: child)
This would delete just the key/value "added"
EDIT
In case of autoID, you need to save the autoID into your Dictionary to be able to use it later.
This is for example one of my functions:
func store(item: MyClassObject) {
var item = item.toJson()
let ref = self.ref.child("MyParentFolder").childByAutoId()
item["uuid"] = ref.key // here I'm saving the autoID key into the dictionary to be able to delete this item later
ref.setValue(item) { error, _ in
if let error = error {
print(error)
}
}
}
Because then I'm having the autoID as part of my dictionary and am able to delete it later:
Then I can use it like .child(MyClassObject.uuid).remove... which is then the automatic generated id.
we can store the randomly generated id as the user id in the database and retrieve that to delete
for e.g. :
let key = ref?.childByAutoId().key
let post = ["uid": key,
"name": myName,
"task": myTask]
ref?.child(key!).setValue(post)
in order to delete
setvalue of the id as nil
for e.g. :
var key = [string]()
ref?.child(key[indexPath.row]).setValue(nil)
key.remove(at: indexPath.row)
myArray.remove(at: indexPath.row)
myTable.reloadData()