How to Update Individual Array Element in Firebase with iOS Swift

How to update individual array element in firebase with iOS Swift?

You can't without reading the entire document, modifying the array data in memory, then updating the entire array field back out do the document. The smallest unit of change in a Firestore array field is an entire field. You can't make smaller changes to individual elements of a field, except those defined by arrayUnion and arrayRemove (which are not compatible with what you're trying to do here).

Firestore Update single item in array of objects

Whenever you want to modify individual elements of an array type field, you have to read the document, modify the array in memory, then update the modified array back to the document.

You will not be able to do this without reading the document first. If you require an atomic update, you can perform this read-then-update procedure in a transaction.

How can i update an object (map) in an array with swift in firestore database?

first of all I want to thanks to @Doug Stevenson for his kindly response.
in my case, I must change the array of the objects to sub collections.

How to update an array of objects with Firestore?

Edit 08/13/2018: There is now support for native array operations in Cloud Firestore. See Doug's answer below.


There is currently no way to update a single array element (or add/remove a single element) in Cloud Firestore.

This code here:

firebase.firestore()
.collection('proprietary')
.doc(docID)
.set(
{ sharedWith: [{ who: "third@test.com", when: new Date() }] },
{ merge: true }
)

This says to set the document at proprietary/docID such that sharedWith = [{ who: "third@test.com", when: new Date() } but to not affect any existing document properties. It's very similar to the update() call you provided however the set() call with create the document if it does not exist while the update() call will fail.

So you have two options to achieve what you want.

Option 1 - Set the whole array

Call set() with the entire contents of the array, which will require reading the current data from the DB first. If you're concerned about concurrent updates you can do all of this in a transaction.

Option 2 - Use a subcollection

You could make sharedWith a subcollection of the main document. Then
adding a single item would look like this:

firebase.firestore()
.collection('proprietary')
.doc(docID)
.collection('sharedWith')
.add({ who: "third@test.com", when: new Date() })

Of course this comes with new limitations. You would not be able to query
documents based on who they are shared with, nor would you be able to
get the doc and all of the sharedWith data in a single operation.

Can you append a string to a Firebase array with swift

Firestore has a special documented operation to append a single item to the end of an array using FieldValue.arrayUnion():

let washingtonRef = db.collection("cities").document("DC")

// Atomically add a new region to the "regions" array field.
washingtonRef.updateData([
"regions": FieldValue.arrayUnion(["greater_virginia"])
])

If you want make any changes to an array other than add or remove a single item, you will have to read the array, modify it, then write it back.

update array value from firebase database

You should reload data after getting updated values from Firebase. Put this in the observeSingleEvent completion: self.tableView.reloadData()

How to add an array to Firebase Firestore Swift

You must pass an array to Firestore to update an array property. What you're doing is passing an element of an array. The remove(at:) method, when used like this:

let removedElement = someArray.remove(at: someIndex)

will remove the element but return the value of that removed element; it doesn't return the updated array. You must first remove the element and then get the truncated array:

someArray.remove(at: someIndex)
let updated = someArray

Therefore, first remove, then update:

data[0].upvotes!.remove(at: data[0].upvotes!.firstIndex(of: MainView.username ?? "Anonymous")! // first remove
db.collection("forum").document(data[0].id!).setData([
"upvotes": data[0].upvotes!) // then pass
])

However, I'd recommend reformatting the code because it doesn't read very well, IMO, and there is too much force unwrapping for my taste.

Update that object with the new info in array and to display tableview Swift

Given the above comment discussion, I think you need to update your watchForChangesInMyFriends method as below to actually update the datasource with the new friend data. You should also do all your UI updates on the main thread, and as there is no guarantee that this closure will run on the main thread you need to force the tableView update onto the main thread.

func watchForChangesInMyFriends() { 
let usersRef = self.ref.child("profiles") usersRef.observe(.childChanged, with: { snapshot in
let key = snapshot.key
if let friendIndex = self.myFriendsDataSource.firstIndex(where: { $0.uid == key} ) {
let friend = self.myFriendsDataSource[friendIndex]
print("found user \(friend.batteryStatus), updating")
self.myFriendsDataSource[friendIndex] = FriendClass(withSnaphot: snapshot)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}

It's also better practice to update just the tableView data that has changed rather than reloading the whole tableView. You can probably use the array index to generate an IndexPath for the appropriate row and then just reload that row. Without seeing your tableView methods I can't be precise, but it'll probably look something like this:

let indexPath = IndexPath(row: friendIndex, section: 0)
DispatchQueue.main.async {
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}

How to update an array on firebase using the onDisconnectUpdateChildValues

First off: arrays like the one you're using here are an anti-pattern in Firebase. I highly recommend reading Best Practices: Arrays in Firebase.

But aside from that: to update on disconnect you need to specify two things:

  1. The exact, complete path that you want to write to.
  2. The exact value that you want to write to that path.

Say you want to remove a value/path at users/2 I'd recommend using:

self.database.child("\(groupChatId)_F/users/2").onDisconnectRemoveValue(){ (error, ref) in
...

This does mean that you need to know the path of the user (2 in this example code). If you don't currently know that path, you have two main options:

  • either to remember it in your code when you create the child node. So when you add "Bob", remember in your code that you added him in node 2.

  • or you can make the path idempotent. For example, if you user names are unique, I'd recommend storing them in a structure like this:

    "users": {
    "Alex": true,
    "Bob": true,
    "John": true
    }


Related Topics



Leave a reply



Submit