You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何等待多个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")
                }
            }
        }
    }
}

关键修改点说明

  1. 嵌套DispatchGroup:每个集合查询完成后,创建内部deleteGroup,遍历文档时调用deleteGroup.enter(),在delete()的回调闭包中调用deleteGroup.leave(),确保该集合下所有文档删除完成后,才通知外部group执行leave()。
  2. 错误处理:添加了删除操作的错误打印,方便排查删除失败的情况。
  3. 安全退出:即使查询失败(snapshot为nil),也会调用group.leave(),避免DispatchGroup永久等待。
  4. Auth删除的错误处理:在Auth.auth().currentUser?.delete()中添加了错误处理,捕获账号删除失败的情况。

内容的提问来源于stack exchange,提问作者RapsIn4

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 23:35:32