How to remove a certain field from map cloud firestore? - swift

I continue to develop a directory application and now I need to delete a specific line in the map from cloud firestore.There is my database:
screenshot
I need to remove field with key "2324141" or change its value.
I know about this method:
"2324141": FieldValue.delete()
But how do I get into the map and delete field or change the value?
thanks!
Here is my model of Object:
class ObjectInFB: Codable {
var objectFromPartsCatalogueListCode: String?
var countOfCurrentObjectInShoppingCart: Int = 1}
Here is func, where i save object in users shoppingBag:
func addToUserShoppingCartFB(user: User?, object: ObjectInFB, count: Int){
guard let user = user else { return }
let objectReadyToBeWriten = [
"UserEmail":"\(user.email!)",
"shoppingCart" : ["\(object.objectFromPartsCatalogueListCode!)" : FieldValue.increment(Int64(count))]] as [String : Any]
db.collection("users")
.document("\(user.uid)")
.setData(objectReadyToBeWriten, merge: true)
{ err in
if let err = err {
print("Error adding document: \(err)")
} else {}
}
}
And after the user in their cart in the application clicks "remove from cart" I want to delete an object from their cart in Firebase. I can only remove any field from the document, but how can I remove a field from the nested ShoppingCart?
func removeShoppingCartFB(object: ObjectInFB, user: User?){
db.collection("users").document(user!.uid).updateData([
"shoppingCart": ["\(object.objectFromPartsCatalogueListCode!)" : FieldValue.delete()],
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}

If the goal is to delete a field within a map, it can be accessed through dot notation. For example suppose your structure looks like this
users
uid_0
UserEmail: "test#test.com"
shoppingCart (a map)
2324141: 10
122323424: 13
and we want to remove the 2324141 field. Here's the Swift code to do that
let myCollection = self.db.collection("users")
let myDoc = myCollection.document("uid_0")
myDoc.updateData( ["shoppingCart.2324141": FieldValue.delete(),
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}

Related

How can I get the ID of an FireStore Document, if i know the value of one field? (In Swift)

I'm programming in Swift and I have a little problem.
I wondered how I can get the ID of an Document, if I have the the value for a field in it.
Little example:
document_x -> name: x, number: 5
document_y -> name: y, number: 2
document_z -> name: z, number 16
And now I know that I want the ID of the document, which has the name: z (in this case document_z)
Is this possible?
To find the document(s) matching a specific value in a field you can use a query. Something like this:
let query = collectionRef.whereField("name", isEqualTo: "z")
query.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
i had same problem some months back.fire base was generating random doc id because of not giving specific id.
there are two approaches to this:
first one : this is how i found my doc id w.r.t to string i have given in it
private func GetFireBaseID(_ ComingToBeDel : String , completion:#escaping(String) -> ()) {
db.collection(FireBaseConstant.ProjectCollectioName).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let currentdata = document.data()
if let currenttitle = currentdata[FireBaseConstant.ProjectVC.ProjectName] as? String {
if currenttitle == ComingToBeDel {
completion(document.documentID)
break
}else {
completion("")
}
}
}
}
}
}
caller is like this
GetFireBaseID(filterobj[index].ProjectTitle) { (returnid) in
if returnid.count > 0 {
db.collection(FireBaseConstant.ProjectCollectioName).document(returnid).updateData([
FireBaseConstant.ProjectVC.ProjectName : string
])
}else {
print("couldnt find your file")
}
}
second method can be using whereField filtered queries.for that please consult following link.
https://firebase.google.com/docs/firestore/query-data/queries

How do I retrieve information from the document of the current logged in user of my app?

While registering/signing up a user, I store data in 3 fields in each users document: Name, email and username.
I have a button, pressing which reveals a welcome message as such: "Hellow, (username)", but this fails to read the username of the current user, ie the user that is currently logged in, and instead displays the username of any other random user.
I can read the email of the current user by using Firebase Auth, ie:
Auth.auth().currentUser?.email
but I cannot read the username of the current user, which I understand is not saved in the Authentication side of Firebase.
Here is the code I've already tried:
func loadUserData() {
guard let email = Auth.auth().currentUser?.email else { return }
emailString = "\(email)"
self.db.collection("users").getDocuments { (snapshot, error) in
if let err = error {
print("Error: \(err)")
} else {
for document in snapshot!.documents {
let emailRetrieved = document.get("email") as! String
let usernameRetrieved = document.get("username") as! String
print("ATTEN: 658")
self.emailString = emailRetrieved
print(self.emailString)
self.usernameString = usernameRetrieved
print(self.usernameString)
}
}
}
}
which is exactly what the documentation suggests. The code above just prints out the email and the username of all the documents under the collection "users."
The name of my document is "users", and each user's information is saved in their individual document which is names as their user ID, if that helps. I'd really be grateful if someone could help, I've been stuck on this issue for a while now.
I would suggest you to create a field with the uid of your users from firebase auth in your document or the document-id itself. In this repo under usersEndpoint.js take a look ath the exports.createUser.
...
usersCollection.doc(userRecord.uid).set({
uid: userRecord.uid,
firstname: user.firstname,
lastname: user.lastname,
dateOfBirth: user.dateOfBirth,
sex: user.sex,
city: user.city,
activities: user.activities,
offers: user.offers
})
...
With this approach you can change your code like this:
func loadUserData() {
...
let uid= Auth.auth().currentUser?.uid
self.db.collection("users").get(uid) { (snapshot, error) in
if let err = error {
print("Error: \(err)")
} else {
...
}
}
}
That way it should be much easier for you to get the right document of your users.
Sorry, that my code is not in swift. It's been a while that I coded in swift. But I hope that helps.
EDIT
Alternatively you could do is to use a query like described in the docs of Firestore. It could look like this:
...
guard let email = Auth.auth().currentUser?.email else { return }
emailString = "\(email)"
self.db.collection("users").whereField("email", isEqualTo: emailString)
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}
...

Firebase Firestore not working in Swift Share Extension

I am building a Share Extension in Swift which saves a document to Firestore. So far I have been able to authenticate the correct user via keychain sharing and app groups. I can also get a documentID from a new document reference:
var ref = Firestore.firestore().collection("stuff").document()
print(ref.documentID) //prints the id
But when I try to save something to Firestore, nothing prints in the console, meaning I get neither a failure or success callback from Firebase (see below where I batch the updates). Here is my ShareController.swift file:
class ShareViewController: SLComposeServiceViewController {
var sharedIdentifier = "asdf"
override func viewDidLoad() {
FirebaseApp.configure()
setupKeychainSharing()
}
func setupKeychainSharing() {
do {
try Auth.auth().useUserAccessGroup(sharedIdentifier)
} catch let error as NSError {
}
}
override func isContentValid() -> Bool {
return true
}
override func didSelectPost() {
if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
if let contents = content.attachments {
for attachment in contents {
if attachment.hasItemConformingToTypeIdentifier(m4aType) {
attachment.loadItem(forTypeIdentifier: m4aType, options: nil, completionHandler: { (results, error) in
if error == nil {
if let url = results as? URL {
if let audioData = NSData(contentsOf: url) {
let fileName = url.lastPathComponent
if Auth.auth().currentUser != nil {
guard let myId = Auth.auth().currentUser?.uid else { return }
let batch = Firestore.firestore().batch()
let ref = Firestore.firestore().collection("projects").document()
let project: [String: Any] = [
"ownerId": myId,
"type" : "audio",
"extensionUrl" : audioUrl.absoluteString
]
batch.updateData(project, forDocument: ref)
let privateRef = Firestore.firestore().collection("user-private").document(myId)
let privateUpdate: [String: Any] = [
"projects" : FieldValue.arrayUnion([ref.documentID])
]
batch.updateData(privateUpdate, forDocument: privateRef)
batch.commit(completion: { (error) in
if let error = error {
print("error updating database: \(error.localizedDescription)")
} else {
print("Database updated successfully!!!!!")
self.extensionContext!.completeRequest( returningItems: [], completionHandler: nil)
}
})
}
}
}
}
})
}
}
}
}
}
}
It appears you're trying to create additional documents within the projects node and update the user-private node. If so, the code in the question won't work.
UpdateData: Updates fields in the document referred to by document. If
document does not exist, the write batch will fail.
Here's a working batch write function that adds a document to a projects node with a Firestore generated document ID and child fields for extension, ownerId and type as well as a user_private collection with a document with a documentId of id_0.
func batchWrite() {
let batch = self.db.batch()
let ref = self.db.collection("projects").document()
let project: [String: Any] = [
"ownerId": "id_0",
"type" : "audio",
"extensionUrl" : "some url"
]
batch.setData(project, forDocument: ref)
let privateRef = self.db.collection("user-private").document("id_0")
let privateUpdate: [String: Any] = [
"projects" : "some projects"
]
batch.setData(privateUpdate, forDocument: privateRef)
batch.commit(completion: { (error) in
if let error = error {
print("error updating database: \(error.localizedDescription)")
} else {
print("Database updated successfully!!!!!")
}
})
}
Note that self.db is a class var reference to my Firestore. That makes it so you don't have to keep typing Firestore.firestore() and use self.db instead.
Also note that a batch is probably not needed in this case as it doesn't appear there are a significant number of writes occurring at the same time.
If you're not using batch, the .addDocument will add documents to collections.
Here's a function that writes a task to a tasks collection and auto-generates a documentId
func writeTask() {
let collectionRef = self.db.collection("tasks")
let data = [
"task": "some task"]
var ref: DocumentReference? = nil
ref = collectionRef.addDocument(data: data, completion: { err in
if let error = err {
print(error.localizedDescription)
}
print(ref?.documentID)
})
}
It is likely Firebase wasn't configured for your Share extension. Print statements do not work for share extension. Instead you should make use of NSLog statements i.e NSLog("refDocId:\(ref.DocumentId)"). Then in your xcode, navigate to Window > Devices and Simulators > Open Console. To configure Firebase in your Share extension, use FirebaseApp.configure()

Problem with deleting account and documents with Firestore

I want to delete the user account and all of its documents in Firestore, but with asynchronous queries, Firebase deletes the account before all documents.
Because of that I get an auth error because when firestorm delete the lasts documents, the user no longer exist.
db.collection("users").document(self.user.uid).collection("sachets").getDocuments() { (QuerySnapshot, err) in
if let err = err{
print("Erreur de lecture : \(err)")
} else {
for document in QuerySnapshot!.documents {
db.collection("users").document(self.user.uid).collection("sachets").document(document.documentID).delete(){ err in
if let err = err {
print(" 🔴 Probleme de suppression des docuemnts \(err)")
} else {
print(" 🔵 Documents supprimés")
}
}
}
}
}
self.user?.delete { error in
if let error = error {
print(" 🔴 Probleme de suppression du compte Utilisateur \(error)")
} else {
print(" 🔵 Utilisateur supprimé")
}
}
Someone can tell me how to do ?
thanks
Alright, I set up a test-project and solve this. You also need to make sure to remove all the data from the user fields. For instance, if you're having name, age etc... at the user node.
First I created a function to fetch the current users uid:
func currentUser() -> String {
return Auth.auth().currentUser!.uid
}
Then I created a function to remove all the data for the logged in user. This will first remove all the subcollection data and then it will remove the document for the userID.
To handle asynchronous methods you can use DispatchGroup to notify when all the asynchronous calls are done.
One side note: Make sure to NEVER force-unwrap a value if you can't 100% guarantee there are documents. Otherwise, your app will likely crash. To solve this either use guard or if let to solve this problem.
// Where "user" is the result from currentUser()
func removeData(from user: String) {
db.collection("users").document(user).collection("sachets").getDocuments { (snapshot, error) in
if let error = error {
// Handle error
} else if let documents = snapshot?.documents {
// Using if let to see if there are documents
// Time to delete all the subCollection for the user
self.deleteSubCollectionData(for: user, documents, completion: {
// Once that done, make sure to delete all the fields on the highest level.
self.db.collection("users").document(user).delete(completion: { (error) in
if let error = error {
// Handle error
} else {
// Delete the account
self.deleteAccount()
}
})
})
}
}
}
// This function will remove the subCollectionData
fileprivate func deleteSubCollectionData(for user: String, _ documents: [QueryDocumentSnapshot], completion: #escaping () -> ()) {
let group = DispatchGroup()
documents.forEach({
group.enter()
self.db.collection("users").document(user).collection("sachets").document($0.documentID).delete(completion: { (error) in
if let error = error {
// Handle error
}
group.leave()
})
})
// Once the dispatchGroup is done...
group.notify(queue: .main) {
completion()
}
}
And in the end...
func deleteAccount() {
Auth.auth().currentUser?.delete { (error) in
if let error = error {
print(error)
} else {
print("Deleted account")
}
}
}
If you don't remove all the levels of data, then there still will be data in Firestore.
Your getDocuments() method is asynchronous, so you should delete the account only after having deleted the documents.
Just put the user?.delete method inside the getDocuments() callback
db.collection("users").document(self.user.uid).collection("sachets").getDocuments() { (QuerySnapshot, err) in
if let err = err{
print("Erreur de lecture : \(err)")
} else {
for document in QuerySnapshot!.documents {
db.collection("users").document(self.user.uid).collection("sachets").document(document.documentID).delete(){ err in
if let err = err {
print(" 🔴 Problème de suppression des documents \(err)")
} else {
print(" 🔵 Documents supprimés")
}
}
}
}
self.user?.delete { error in
if let error = error {
print(" 🔴 Problème de suppression du compte Utilisateur \(error)")
} else {
print(" 🔵 Utilisateur supprimé")
}
}
}

Why does my firestore collection return 0 documents

Here is my database.
This is my code
let db = Firestore.firestore()
let defaults = UserDefaults.standard
let userId: String! = defaults.string(forKey: "UserUUID")
db.collection("Portfolios")
.whereField("users." + userId, isEqualTo: true)
.limit(to: 1)
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for portfolioDocument in querySnapshot!.documents {
print("Portfolio: \(portfolioDocument.documentID) => \(portfolioDocument.data())")
let portRef = db.collection("Portfolios").document("portfolioDocument.documentID")
portRef.getDocument { (document, error) in
if let document = document {
print("Document data: \(document.data())")
portRef.collection("Accounts").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
var docs:[DocumentSnapshot] = []
for document in querySnapshot!.documents {
I have even harde coded the "Mogu..." Document ID to no effect. What is the obvious thing I am missing?
querySnapshot.documents is 0 at the last line:
for document in querySnapshot!.documents {
EDIT: It is the last query that returns 0 documents. The rest work:
no, that has no effect on the top level as that is successful. It is the last query that is brings 0 documents: portRef.collection("Accounts").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
var docs:[DocumentSnapshot] = []
for document in querySnapshot!.documents {
The Coinbase and Default documents in your Accounts collection don't actually exist, see non-existent docs in the console. In your screenshot, notice that the document names for Coinbase and Default are italicized and grayed out. This indicates that these documents weren't directly created but that they are on the path of a document nested deeper down which does exist.
Try setting a field value within Coinbase and Default. This should get them to appear in your query.