问题描述
所以我的目标是在用户删除他们的帐户时,如果购买的门票的客人数超过 1,则删除所有用户的客人。
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()
}
我提高了重试限制,看看这是否是问题所在,但如果有多个来宾,它仍然不会删除。
当用户在删除其帐户之前成功重新进行身份验证时,我会在警报操作中调用它:
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)
}
这是数据库中的结果:
其他所有内容都可以正常删除以正确的顺序、购买、用户本身,然后是用户退出 Firebase 身份验证,但如果是的话,客人永远不会被删除超过 1 位客人。是不是我在 deleteUserGuests
方法中做错或遗漏了什么导致了这个问题?
解决方法
正如我多次说过的那样,我会以不同的方式处理整个任务——我会在服务器端进行这种清理,使用批处理或事务操作以原子方式执行删除,并且具有健壮性全程递归。但是,为了解决您为什么不能删除此子集合中的文档的直接问题,这样做可以做到。
func deleteUserGuests(completion: @escaping (_ done: Bool) -> Void) {
guard let user = Auth.auth().currentUser else {
return
}
var retries = 0
func task() {
db.collection("student_users/\(user.uid)/events_bought").getDocuments { (snapshot,error) in
if let snapshot = snapshot {
if snapshot.isEmpty {
completion(true)
} else {
let dispatchEvents = DispatchGroup()
var errors = false
for doc in snapshot.documents {
dispatchEvents.enter()
self.db.collection("student_users/\(user.uid)/events_bought/\(doc.documentID)/guests").getDocuments { (snapshot,error) in
if let snapshot = snapshot {
if snapshot.isEmpty {
dispatchEvents.leave()
} else {
let dispatchGuests = DispatchGroup()
for doc in snapshot.documents {
dispatchGuests.enter()
doc.reference.delete { (error) in
if let error = error {
print(error)
errors = true
}
dispatchGuests.leave()
}
}
dispatchGuests.notify(queue: .main) {
dispatchEvents.leave()
}
}
} else {
if let error = error {
print(error)
}
errors = true
dispatchEvents.leave()
}
}
}
dispatchEvents.notify(queue: .main) {
if errors {
retries += 1
run()
} else {
completion(true)
}
}
}
} else {
if let error = error {
print(error)
}
retries += 1
run()
}
}
}
func run() {
guard retries < 30 else {
completion(false)
return
}
if retries == 0 {
task()
} else {
let delay = Double(retries)
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
task()
}
}
}
run()
}