Firestore batch set is only inserting the last item - swift

I'm trying to batch create a bunch of documents from the array items, where each item is a map of key-value pairs. I've got multiple values in my array, but for some reason the only item that ever gets inserted into the db is the last item in the array.
What am I doing wrong here? Or can I only use setData once in the history of the commit?
let wordsRef = db.collection("items").document()
for item in items {
batch.setData(item, forDocument: wordsRef)
}
batch.commit() { err in
// error reporting
}
Thanks heaps!

I had the same problem a while ago. Just simply move the wordsRef inside the for loop, like I have done below. Hopefully this helps...
Code:
for item in items {
let wordsRef = db.collection("items").document()
batch.setData(item, forDocument: wordsRef)
}
batch.commit() { err in
// error reporting
}

Related

ParseSwift ParseObject QueryConstraint

I have two collections in a mongo DB.
Here is how a document looks in the first collection (MainCollection):
_id
:"mzWqPEDYRU"
TITLE
:"ZAZ: I want."
ownerID
:"lGutCBY52g"
accessKey
:"0kAd4TOmoK0"
_created_at
:2020-03-13T11:42:11.169+00:00
_updated_at
:2020-03-13T17:08:15.090+00:00
downloadCount
:2
And here is how it looks in the second collection (SecondCollection):
_id
:"07BOGA8bHG"
_p_unit
:"MainCollection$mzWqPEDYRU"
SENTENCE
:"I love nature peace and freedom."
Order
:5
ownerID
:"lGutCBY52g"
AUDIO
:"07067b5589d1edd1d907e96c1daf6da1_VOICE.bin"
_created_at
:2020-03-13T11:42:17.483+00:00
_updated_at
:2020-03-13T11:42:19.336+00:00
There is a parent children relationship between the first and the second collection. In the last document we can see the _p_unit field where the "mzWqPEDYRU" part points to the id of the parent in the first collection.
I have one problem from start with the following code:
func theFunction() {
do {MainCollection.query().find() {
result in
switch result {
case .success(let items):
print("items.count = \(items.count)")
for item in items {
/// ....
}
case .failure(let error):
print("Error in \(#function): \(error)")
}
}
}
}
The way this above code is written works fine and I get the number of elements in MainCollection as one would expect. But then comes a less expected behaviour, in this same code if I replace MainCollection by SecondCollection, instead of getting the number of elements in SecondCollection as I would think. I get an error like:
ParseError(code: ParseSwift.ParseError.Code.unknownError,
message: "Error decoding parse-server response:
Optional(<NSHTTPURLResponse: 0x2837211a0> { URL:}
{ Status Code: 200, Headers {} }) with error:
The data couldn’t be read because it isn’t in the correct format.
Format: Optional(\"{\\\"results\\\": .......
Can anybody point out what is causing this?
It is something like:
var SecondCollection.query(unit == documentOne).find()
The .query() method works in a key/value scheme so it should pass the key as a string and the value as the referenced type, so passing "unit" between double quotes is correct:
do {SecondCollection.query("unit" == cell).find() {
The error you're getting is because cell is a Parse.Object and it is expecting a value in that place (a property in this case).
Please try the following and see if it works for you:
do {SecondCollection.query("unit" == cell.id).find() {

Delete a specific document, not the all Firestrore collection

There is a table view that displays a collection of user flowers.
When user goes to detail VC, he can see information about the selected flower, also there is a "Delete" button, the problem is that I only found how to delete all flowers (all collection MyFlowers),
db.collection("users").document(Auth.auth().currentUser!.uid).collection("MyFlowers").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
document.reference.delete()
}
}
}
but I want delete only selected flower.
To delete a specific document, you'll need to know the ID of that document or something else that uniquely identifies the specific document.
So you'll need to take the document ID that you pass to the VC and use that in the call to delete the specific document:
db.collection("users").document(Auth.auth().currentUser!.uid)
.collection("MyFlowers").document("idOfTheDocumentToDelete")
.delete()

How to display specific data, and group it together in firestore

I'm extremely new to firebase and need to display all the data in my collection. Within my app there is an integrated quiz function, and when the 'submit score' button is pressed, the data is sent to Firestore as a new document based on the uid.
user collection in firebase, new document based on uid .
This is what I have so far:
func getData() {
let db = Firestore.firestore()
// Get data
db.collection("users").getDocuments()
{
(querySnapshot, err) in
if let err = err
{
print("Error getting documents: \(err)");
}
else
{
for document in querySnapshot!.documents {
self.studentlbl.text = ("\(document.documentID) => \(document.data())");
}
}
}
This displays the following: result
I'm trying to figure out how to display the first name, followed by the user's corresponding score.
Thanks
You can display the specific field by adding field name in document.data() or doc.data() or the example below:
document.data().firstname;
or in your case(swift) if I'm correct:
self.studentlbl.text = ("(document.data().firstname");
Regarding to the score of the users, I'll recommend creating a new collection to store the data of quiz scores for every users. You can use this answer for user and post as the reference and example that can help you how you can build the database structure of your application. The answer also include how you will query or group it together.

Check if a new field was added in a specific document like documentChanges for a collection in Firestore

I use this code for load comments in a table view:
func observePostComments(postId: String, completion: #escaping (String) -> Void) {
let db = Firestore.firestore()
db.collection("post-comments").document(postId).addSnapshotListener { (snapshot, err) in
if snapshot!.exists {
for key in (snapshot?.data()!.keys)! {
completion(key)
}
} else {
return
}
}
}
It works like it should, but every time a user creates a new comment, all comments are added again. I know how it works for a collection with:
querySnapshot?.documentChanges.forEach { diff in
if (diff.type == .added) { ....
But I can not figure out how to implement that functionality on a document / field level. If I want to do the same on a document level, I receive
Value of type 'DocumentSnapshot?' has no member 'documentChanges'.
How can I track changes on a specific document level, when a new Key-Value pair was added to a document?
Firestore's change detection only works on complete documents. If you need to know what changed inside a document, you will have to detect this in your own code, for example by comparing the previous DocumentSnapshot with the new one.
The exact way to do this depends a bit on what data you store, but there are two broad approaches:
You take something that is unique about each comment, and check if that's already present in your UI. This can for example be the ID of each comment, but anything else that's unique works too.
You store a timestamp for each comment, and keep track of the most recent timestamp you've already processed. Then in an update, you skip all comments up until that timestamp.
Another approach would be to clear the UI before adding the same comments to it. So something like:
db.collection("post-comments").document(postId).addSnapshotListener { (snapshot, err) in
if snapshot!.exists {
clearCommentsFromUI() // this is a function you will have to implement
for key in (snapshot?.data()!.keys)! {
completion(key)
}

How to store data in Firestore (Swift)

I have an iOS app using Cloud Firestore and have problems with updating the data. My goal is to add urls to a dictionary one by one, but all I get is rewritten one value. How should I use setData and updateData? Tried it different ways
storageRef.child("users/" + currentUser.value!.documentID + "/" + faceRef.documentID + ".jpg")
.putData(data!).observe(.success) { (snapshot) in
guard let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString else { return }
let db = self.fsReference.document(self.currentUser.value!.documentID)
var dict = ["faces": ["": ""]]
dict["faces"] = ["newvalue\(downloadURL.hashValue)": downloadURL]
db.updateData(dict)
completion?()
Here's what I tried. Any advice would be nice, thanks in advance!
UPD: Tried to move my dictionary to subcollection, but after .collection("newCollection").document("newdocument") collection does not appear. What might be the problem?
So what I am seeing is you are using cloud storage to save profile pictures and you want to save each one of the urls those pictures. You need to understand that both setValue() and updateValue() do just about the same thing. A note with updateValue() is it will create that document if it doesn't already exist. So, when updating values in Firestore understand that it sets the value to what you give it, which can be misleading at first.
1st When updating any document start by getting the document first. If people are constantly updating different document you may want to consider using Firestore transactions: https://firebase.google.com/docs/firestore/manage-data/transactions#transactions
This will make sure that your data is updated correctly.
2nd Append the URL to the to the array, I am not how you set it up, but I would setup the firestore to look something like this
"users" = [
"unique_id = "{
"firstname": "John",
"lastname": "Doe",
"unique_id": "document_id_here"
"faces": [ {key: value} ]
}
]
When you serialize that object your faces object should be this [[String: Any]]
3rd, last step would be to get the document and update just that value
// Get the value in the completion with the data use this code
// Drill down to the property you want to update using the completion data ex.
var faces = completedData.faces
faces.append("[key: value]")
// Update the data back to firestore
let path = Firestore.firestore().collection("users").document("unique_user_id")
// Merging is so important. otherwise it will override your document
path.setData(["facesKey: faces"], merge: true) {(error in
if let error = error {
// good error handling here
}
// Successfully updated document
)}