【问题标题】:async deletion task won't work if guest count is more than 1如果访客数量超过 1,异步删除任务将不起作用
【发布时间】:2021-04-27 16:52:21
【问题描述】:

所以我的目标是在用户删除其帐户时,如果已购买门票的客人数量超过 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 方法中是否有我做错或遗漏的事情导致了这个问题?

【问题讨论】:

  • 你为什么不嵌套这些任务,以便一个接一个地开始,然后在最后一个任务中删除用户对象,然后在完成处理程序中停止动画微调器并继续用户?或者这不是你想要的方式?
  • 嗯,这能解决这个问题吗?如果确实如此,那么我肯定会那样做,但如果不是,我真的看不到它的用途。我唯一需要弄清楚的是如何让 for 循环接收所有客人并删除它们。听起来很简单,但对于我的生活来说,我无法弄清楚为什么。 @bxod
  • deleteUserGuests 是您唯一遇到问题的函数吗?
  • 是的,实际上是唯一的问题,这就是为什么它让我如此沮丧,其他一切都删除得很好。我搜索了带有调度组的嵌套循环,遇到this question,试图实现答案,仍然没有运气。 @bxod

标签: swift google-cloud-firestore nested-loops grand-central-dispatch dispatchgroup


【解决方案1】:

正如我多次说过的,我会以不同的方式处理整个任务——我会在服务器端进行这种清理,使用批处理或事务操作以原子方式执行删除,并且具有健壮性整个递归。但是,为了解决您无法删除此子集合中的文档的直接问题,可以这样做。

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()
}

【讨论】:

  • 刚刚试了一下,数据库中仍然留下一位客人。听着,我肯定会试试你推荐的东西,事情是,我完全不知道如何或从哪里开始你推荐的东西。你做这件事的时间比我长得多,所以当你解释它时它并不像听起来那么容易哈哈。 @bxod
  • 如果所有其他文档都被此函数删除并且没有引发任何错误,则没有理由保留访客文档。如果此功能没有产生任何错误并且它正在删除除一个访客文档之外的所有文档,那么您的数据或控制台有问题。基本上,您的问题不在此代码中。
  • Welp,我想我现在只能放弃这个了。我只会提示用户在删除帐户之前退还所有购买的费用。我真的不知道如何解决这个问题,不管问题出在哪里。无论如何谢谢。 @bxod
  • 只需使用虚拟文档和虚拟子集合创建一个虚拟集合,然后编写一个循环来删除它们。此处的代码将执行此操作。就像我之前说的,从头开始,循序渐进,一路测试。
猜你喜欢
  • 2012-05-18
  • 1970-01-01
  • 2015-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多