So my goal is to delete all user's guests if the guest count for a purchased ticket is over 1 when a user is deleting their account.
Currently I have this function to try to accomplish this:
func deleteUserGuests(completion: @escaping (_ done: Bool) -> Void) {
var retries = 0
guard let user = Auth.auth().currentUser else { return }
func checkForGuestsAndDeleteIfAny() {
db.collection("student_users/\(user.uid)/events_bought").getDocuments { (querySnapshot, error) in
if let snapshot = querySnapshot {
if snapshot.isEmpty {
completion(true)
// done, nothing left to delete
} else {
// delete the documents using a dispatch group or a Firestore batch delete
for document in querySnapshot!.documents {
let docID = document.documentID
self.db.collection("student_users/\(user.uid)/events_bought/\(docID)/guests").getDocuments { (querySnap, error) in
guard querySnap?.isEmpty == false else {
print("The user being deleted has no guests with his purchases.")
return
}
let group = DispatchGroup()
for doc in querySnap!.documents {
let guest = doc.documentID
group.enter()
self.db.document("student_users/\(user.uid)/events_bought/\(docID)/guests/\(guest)").delete { (error) in
guard error == nil else {
print("Error deleting guests while deleting user.")
return
}
print("Guests deleted while deleting user!")
group.leave()
}
}
}
}
checkForGuestsAndDeleteIfAny()// call task again when this finishes
// because this function only exits when there is nothing left to delete
// or there have been too many failed attempts
}
} else {
if let error = error {
print(error)
}
retries += 1 // increment retries
run() // retry
}
}
}
func run() {
guard retries < 30 else {
completion(false) // 5 failed attempts, exit function
return
}
if retries == 0 {
checkForGuestsAndDeleteIfAny()
} else { // the more failures, the longer we wait until retrying
DispatchQueue.main.asyncAfter(deadline: .now() + Double(retries)) {
checkForGuestsAndDeleteIfAny()
}
}
}
run()
}
I upped the retry limit, to see if that was the issue, but it still doesn't delete the guests if there are more than one.
I call it in an alert action when the user successfully reauthenticates before deleting their account:
let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { (deletion) in
self.deleteButton.isHidden = true
self.loadingToDelete.alpha = 1
self.loadingToDelete.startAnimating()
self.deleteUserGuests { (response) in
if response == false {
return
}
}
self.deleteUserPurchases { (purchase) in
if purchase == false {
return
}
}
self.deleteUserOutOfFirestore { (removed) in
if removed == false {
return
}
}
user.delete(completion: { (error) in
guard error == nil else {
print("There was an error deleting user from the system.")
return
}
print("User Deleted.")
})
self.loadingToDelete.stopAnimating()
self.performSegue(withIdentifier: Constants.Segues.studentUserDeletedAccount, sender: self)
}
This is the result in the database:
Everything else gets deleted fine in correct order, purchases, the user itself, and then the user out of Firebase auth, but the guests never get deleted if it is over 1 guest. Is there something I did wrong or left out in the deleteUserGuests
method that is causing this issue?
As I've said a number of times, I'd approach this entire task differently--I'd do this sort of cleanup on the server side, perform the deletes atomically using a batch or transaction operation, and have robust recursion throughout. However, to fix your immediate problem of why you can't delete the documents in this subcollection, this will do it.