如何等待多个Firebase查询完成后再执行用户账号删除操作?
解决Firebase删除用户账号时数据残留的问题
调用deleteUserAccount()方法时,期望先删除用户所有存储数据,完成后再执行登出并删除账号操作。但当前点击“删除账号”按钮后,账号已被删除,Firebase上的数据却仍有残留——因为deleteAllUserTicketData()中的查询及文档删除操作还未完成,完成回调就已经触发了。需要实现等待所有查询执行完毕、文档全部删除后,再触发回调执行后续的登出和账号删除操作。
原代码如下:
func deleteAllUserTicketData(completion: @escaping () -> Void) { let group = DispatchGroup() group.enter() self.rootWsrCollection?.whereField("uid", isEqualTo: userIdRef).getDocuments { (querySnapshot, err) in guard let snapshot = querySnapshot else { return } for wsr in snapshot.documents{ print("Deleting WSR: \(wsr)") wsr.reference.delete() } group.leave() } group.enter() self.rootExerciseCollection?.whereField("uid", isEqualTo: userIdRef).getDocuments { (querySnapshot, err) in guard let snapshot = querySnapshot else { return } for exercise in snapshot.documents { print("Deleting Exercise: \(exercise)") exercise.reference.delete() } group.leave() } group.enter() self.rootWorkoutsCollection?.whereField("uid", isEqualTo: userIdRef).getDocuments { (querySnapshot, err) in guard let snapshot = querySnapshot else { return } for workout in snapshot.documents { print("Deleting Workout: \(workout)") workout.reference.delete() } group.leave() } group.notify(queue: DispatchQueue.global()) { [weak self] in self?.workoutsCollection.daysCollection.removeAll() print("Calling Notify Method") completion() } } //This method will only delete the user from the Firestore, but will not delete the User Auth credentials. func deleteUserFromFirestore(completion: @escaping () -> Void) { self.rootUserCollection?.whereField("uid", isEqualTo: userIdRef).getDocuments { (querySnapshot, err) in guard let snapshot = querySnapshot else { return } for user in snapshot.documents { print("Deleting User: \(user)") user.reference.delete() } completion() } } func deleteUserAccount() { deleteAllUserTicketData { print("First Completion is called") self.deleteUserFromFirestore { print("Second Completion is called") Auth.auth().currentUser?.delete() } } }
问题根源
当前代码的核心问题是:wsr.reference.delete()、exercise.reference.delete()这类文档删除操作是异步的,但你在查询到文档后直接遍历发起删除,不等删除完成就调用了group.leave(),导致外部的DispatchGroup提前认为所有操作完成,触发completion回调,进而提前执行账号删除。
修复方案
需要在每个集合的删除流程中,额外嵌套一个DispatchGroup,等待该集合下所有文档的删除操作完成后,再调用外部DispatchGroup的leave()方法。同时,deleteUserFromFirestore方法也要做同样的修改,确保用户文档删除完成后才触发回调。
修改后的完整代码
func deleteAllUserTicketData(completion: @escaping () -> Void) { let group = DispatchGroup() // 处理WSR集合 group.enter() self.rootWsrCollection?.whereField("uid", isEqualTo: userIdRef).getDocuments { (querySnapshot, err) in guard let snapshot = querySnapshot else { group.leave() return } let deleteGroup = DispatchGroup() for wsr in snapshot.documents { deleteGroup.enter() print("Deleting WSR: \(wsr)") wsr.reference.delete { error in if let error = error { print("Failed to delete WSR: \(error.localizedDescription)") } deleteGroup.leave() } } deleteGroup.notify(queue: .global()) { group.leave() } } // 处理Exercise集合 group.enter() self.rootExerciseCollection?.whereField("uid", isEqualTo: userIdRef).getDocuments { (querySnapshot, err) in guard let snapshot = querySnapshot else { group.leave() return } let deleteGroup = DispatchGroup() for exercise in snapshot.documents { deleteGroup.enter() print("Deleting Exercise: \(exercise)") exercise.reference.delete { error in if let error = error { print("Failed to delete Exercise: \(error.localizedDescription)") } deleteGroup.leave() } } deleteGroup.notify(queue: .global()) { group.leave() } } // 处理Workouts集合 group.enter() self.rootWorkoutsCollection?.whereField("uid", isEqualTo: userIdRef).getDocuments { (querySnapshot, err) in guard let snapshot = querySnapshot else { group.leave() return } let deleteGroup = DispatchGroup() for workout in snapshot.documents { deleteGroup.enter() print("Deleting Workout: \(workout)") workout.reference.delete { error in if let error = error { print("Failed to delete Workout: \(error.localizedDescription)") } deleteGroup.leave() } } deleteGroup.notify(queue: .global()) { group.leave() } } group.notify(queue: DispatchQueue.global()) { [weak self] in self?.workoutsCollection.daysCollection.removeAll() print("Calling Notify Method") completion() } } // 删除Firestore中的用户文档 func deleteUserFromFirestore(completion: @escaping () -> Void) { self.rootUserCollection?.whereField("uid", isEqualTo: userIdRef).getDocuments { (querySnapshot, err) in guard let snapshot = querySnapshot else { completion() return } let deleteGroup = DispatchGroup() for user in snapshot.documents { deleteGroup.enter() print("Deleting User: \(user)") user.reference.delete { error in if let error = error { print("Failed to delete User: \(error.localizedDescription)") } deleteGroup.leave() } } deleteGroup.notify(queue: .global()) { completion() } } } func deleteUserAccount() { deleteAllUserTicketData { print("All user data deleted") self.deleteUserFromFirestore { print("User document deleted from Firestore") Auth.auth().currentUser?.delete { error in if let error = error { print("Failed to delete auth account: \(error.localizedDescription)") } else { print("User account deleted successfully") } } } } }
关键修改点说明
- 嵌套DispatchGroup:每个集合查询完成后,创建内部
deleteGroup,遍历文档时调用deleteGroup.enter(),在delete()的回调闭包中调用deleteGroup.leave(),确保该集合下所有文档删除完成后,才通知外部group执行leave()。 - 错误处理:添加了删除操作的错误打印,方便排查删除失败的情况。
- 安全退出:即使查询失败(
snapshot为nil),也会调用group.leave(),避免DispatchGroup永久等待。 - Auth删除的错误处理:在
Auth.auth().currentUser?.delete()中添加了错误处理,捕获账号删除失败的情况。
内容的提问来源于stack exchange,提问作者RapsIn4
相关产品推荐
相关产品推荐

