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:
- The exact, complete path that you want to write to.
- 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 node2
.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
Which View Controller Should I Pass in My Swift Native Code for a Flutter Plugin
Circular Button Becomes Rounded Rectangle After Size Increase
How to Crop Image in Circle Shape with Grid Inside Circle
How to Parse a Iso 8601 Duration Format in Swift
Making the Map Zoom to User Location and Annotation (Swift 2)
Disable Long Press Back Button (Callout Menu)
Indexing into Array of Functions: Expression Resolves to an Unused L-Value
Binary Operator + Cannot Be Applied to Operands of Type Cgfloat Int
Updating Label Takes Too Long (Swift)
Swift:How to Find the Position(X,Y) of a Letter in a Uilabel
Getting Uitableview Error "Unable to Dequeue a Cell with Identifier Cell"
Add View as a Parameter to a Custom Viewmodifier
Swift Language Multicast Delegate
How to Handle Usernotifications Actions in iOS 10