Delete a specific document, not the all Firestrore collection - swift

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()

Related

How to update partial of profile using Firestore

I'm creating Edit profile page using Firestore.
When user navigate to the Edit profile, the user have their profile. Because they already registered.
User can change all items in their profile in edit page.
But of course they can change partial of their profile.
How do I detect data they did change?
First I thought I would use update method below
func updateProfile(user: User){
let user = db.collection("user").document("user-id")
user.updateData([
"name":user.name,
"profile":user.profile ?? ""
"phone":user.phone ?? "",
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}
It is possible user did change only their name.
But this way can update not changed data.
I mean I will update all datas user didn't change.
How do I detect the only data user did change.

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.

Firestore Security Rule to grant user read/write permissions on a document and its subcollection not working

I have a Firestore collection named UserProfiles which stores the profile information for each user in individual documents. Each document is named with the user id of the user it belongs to. Each document also contains a number of sub collections as well. I'd like to make rules giving each user the ability to read and write his own document and sub collections under his document ONLY. However when a user tries to read his profile document I keep getting the following error message:
Error getting documents: Error Domain=FIRFirestoreErrorDomain Code=7 "Missing or insufficient permissions." UserInfo={NSLocalizedDescription=Missing or insufficient permissions.}
Here is the rule I created:
service cloud.firestore {
match /databases/{database}/documents {
//grant users read and write access to anything under own profiles collection
match /UserProfiles/{userId}/{document=**} {
allow read, write: if request.auth.uid == userId;
}
//grant users read and write access to their own profile document
match /UserProfiles/{userId} {
allow read, write: if request.auth.uid == userId;
}
}
}
Grateful if anyone could point out where I've gone wrong - Thanks!
EDIT: Sorry - here is the code on the client side in Swift:
let firebaseAuth = Auth.auth()
if firebaseAuth.currentUser != nil {
userUId = Auth.auth().currentUser!.uid
print("User id is \(String(describing: userUId))")
} else {
print("User is not currently logged in")
return
}
//Getting my user info
db.collection("UserProfiles").whereField("user_id", isEqualTo: self.userUId).getDocuments() { (snapshot, error) in
if let error = error {
print("Error getting documents in func viewWIllAppear with user id \(self.userUId): \(error)")
} else {
print("Successful db connection")
for document in snapshot!.documents {
self.myUserHandle = document["user_handle"]! as! String
print("Retrieved user handle is \(self.myUserHandle)")
}
}
}
And the Database structure is:
UserProfiles
|
|--FHEneuY3nron3Ns2Ndl1SGdg9Nsw
|
|--UserHistory
|
|--UserReports
|
|--UserLogs
Basically what I want to do with my rules is give a logged in user read and write permissions on his own profile document which is names with his user id (FHEneuY3nron3Ns2Ndl1SGdg9Nsw), and also on the subcollections beneath it (UserHistory, UserReports and UserLogs), but not allow users to read or write to other user's profile documents or their subcollections.
And then here is the result output to the console:
user id is FHEneuY3nron3Ns2Ndl1SGdg9Nsw
2020-05-26 18:53:58.560680-0400 MealFleet[2495:1086294] 6.2.0 - [Firebase/Firestore][I-FST000001] Listen for query at UserProfiles failed: Missing or insufficient permissions.
Error getting documents in func viewWIllAppear with user id FHEneuY3nron3Ns2Ndl1SGdg9Nsw: Error Domain=FIRFirestoreErrorDomain Code=7 "Missing or insufficient permissions." UserInfo={NSLocalizedDescription=Missing or insufficient permissions.}
UPDATE: Here is the corrected query thanks to Doug for pointing our my error:
db.collection("UserProfiles").document(self.userUId).getDocument() { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data()
self.myUserHandle = (dataDescription!["user_handle"] as? String)!
print("Retrieved user handle is \(self.myUserHandle)")
} else {
print("Error getting documents in func viewWIllAppear with user id \(self.userUId): \(error)")
}
}
Your query does not match your rules. Your query is attempting to filter for all documents where the document field user_id is equal to some string self.userUId. Your rules are requiring that a user may only access a single specific document in UserProfiles where the document ID matches their UID. These are not the same thing at all.
If you want your code to access the user's specific document, then it should not use whereField at all. It just needs to get the individual document using it ID.
db.collection("UserProfiles").document(self.userUId).getDocument()
See the documentation for getting a document.

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)
}

Firestore batch set is only inserting the last item

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
}