firestore文件删除并因逻辑错误而崩溃,但没有逻辑就可以删除 [英] firestore document deletes and crashes with an error in logic but deletes fine without logic

查看:67
本文介绍了firestore文件删除并因逻辑错误而崩溃,但没有逻辑就可以删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我的目标是在条件为假且没有错误的情况下删除Firestore文档.首先,我具有删除Firestore文档的功能:

So my goal is to delete a firestore document if the condition is false with no errors. At first, I had this function for deleting firestore documents:

  override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    
   
    let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (deleted, view, completion) in
        let alert = UIAlertController(title: "Delete Event", message: "Are you sure you want to delete this event?", preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (cancel) in
            self.dismiss(animated: true, completion: nil)
        }
        let deleteEvent = UIAlertAction(title: "Delete", style: .destructive) { (deletion) in
            guard let user = Auth.auth().currentUser else { return }
            let documentid = self.documentsID[indexPath.row].docID
//                let eventName = self.events[indexPath.row].eventName
            let deleteIndex = client.index(withName: IndexName(rawValue: user.uid))
            
            deleteIndex.deleteObject(withID: ObjectID(rawValue: self.algoliaObjectID[indexPath.row].algoliaObjectID)) { (result) in
                if case .success(let response) = result {
                    print("Algolia document successfully deleted: \(response.wrapped)")
                }
            }

            
            self.db.document("school_users/\(user.uid)/events/\(documentid)").delete { (error) in
                guard error == nil else {
                    print("There was an error deleting the document.")
                    return
                }
                print("Deleted")
            }
            
            self.events.remove(at: indexPath.row)
            tableView.reloadData()
            
        }

        alert.addAction(cancelAction)
        alert.addAction(deleteEvent)
        self.present(alert, animated: true, completion: nil)
    }

    deleteAction.backgroundColor = UIColor.systemRed
    

    let config = UISwipeActionsConfiguration(actions: [deleteAction])
    config.performsFirstActionWithFullSwipe = false
    return config

}

因此,这会删除单元格以及云存储区中的文档.现在,我在我的应用程序中遇到一个问题,如果学校用户想要删除他们创建的活动,但某些学生用户已经购买了该活动,则该活动将删除是,但是如果购买了该活动的学生用户尝试查看他们购买的事件的值将为 nil 并且应用程序将崩溃,因为他们购买的事件的详细信息仅取决于创建的事件(这不是我不会更改的概念,只是该应用程序的工作方式).

So this deletes the cell fine and the document in cloud firestore as well. Now i came across an issue in within my app, if a school user wants to delete an event they created but some student users have already purchased this event, this event will delete yes, but if the student user who purchased the event tries to view the event they purchased, the values will be nil and the app will crash because the details of the event they purchased solely rely on the event created (this is not a concept I will not be changing, it's just how the app works).

已编辑代码,为了解决此问题,我决定为删除过程添加一些逻辑:

EDITED CODE In order to fix this I decided to add some logic to the deletion process:

   override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    
    
    let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { (deleted, view, completion) in
        let alert = UIAlertController(title: "Delete Event", message: "Are you sure you want to delete this event?", preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (cancel) in
            self.dismiss(animated: true, completion: nil)
        }
        let deleteEvent = UIAlertAction(title: "Delete", style: .destructive) { (deletion) in
            guard let user = Auth.auth().currentUser else { return }
            let documentid = self.documentsID[indexPath.row].docID
            let eventName = self.events[indexPath.row].eventName
            let deleteIndex = client.index(withName: IndexName(rawValue: user.uid))
            
            
            self.getTheSchoolsID { (id) in
                guard let id = id else { return }
                self.db.collection("student_users").whereField("school_id", isEqualTo: id).getDocuments { (querySnapshot, error) in
                    guard error == nil else {
                        print("Couldn't fetch the student users.")
                        return
                    }
                    for document in querySnapshot!.documents {
                        let userUUID = document.documentID
                        self.db.collection("student_users/\(userUUID)/events_bought").whereField("event_name", isEqualTo: eventName).getDocuments { (querySnapshotTwo, error) in
                            guard error == nil else {
                                print("Couldn't fetch if users are purchasing this event")
                                return
                            }
                            guard querySnapshotTwo?.isEmpty == true else {
                                self.showAlert(title: "Students Have Purchased This Event", message: "This event cannot be deleted until all students who have purchased this event have completely refunded their purchase of this event. Please be sure to make an announcement that this event will be deleted.")
                                return
                            }
                        }
                    }
                    
                    deleteIndex.deleteObject(withID: ObjectID(rawValue: self.algoliaObjectID[indexPath.row].algoliaObjectID)) { (result) in
                        if case .success(let response) = result {
                            print("Algolia document successfully deleted: \(response.wrapped)")
                        }
                    }
                    
                    
                    self.db.document("school_users/\(user.uid)/events/\(documentid)").delete { (error) in
                        guard error == nil else {
                            print("There was an error deleting the document.")
                            return
                        }
                        print("Deleted")
                    }
                    self.events.remove(at: indexPath.row)
                    tableView.reloadData()
                }
            }
        }
        
        alert.addAction(cancelAction)
        alert.addAction(deleteEvent)
        self.present(alert, animated: true, completion: nil)
    }
    
    deleteAction.backgroundColor = UIColor.systemRed
    
    
    let config = UISwipeActionsConfiguration(actions: [deleteAction])
    config.performsFirstActionWithFullSwipe = false
    return config
    
}

更新因此,如果查询为空,我将完全退出循环,然后继续进行删除过程.现在,我以学生用户的身份购买了一个事件,然后尝试删除与学校用户相同的事件,对此进行了测试.由于某种原因,当我在第一个警报中按下删除操作时,该事件首先被删除,然后紧接着显示验证警报,但没有任何崩溃.是的,崩溃消失了,这很好,但是我查询中的 return 方法实际上并没有返回并脱离删除方法,它先删除然后显示错误,但我没有得到

UPDATE So I completely quit out of the loop and then would move forward with the deletion process if the query was empty. Now I tested this by purchasing an event as a student user, and then trying to delete the same event as a school user. For some reason when I pressed the delete action in the first alert, the event deleted first and then the validation alert showed up right after but crashed with no errors. Yes, the crash went away which is great but the return method in my query doesn't actually return and break out of the deletion method, it deletes and then shows the error, which I don't get.

有什么建议吗?

推荐答案

好的,我想我发现了问题.当您浏览代表某所学校的用户的每个文档时,请为每个用户检查 他们是否正在参加试图删除的活动.问题是,每次您执行此检查以查看用户是否正在参加活动时,您还需要执行以下一项操作(根据 querySnapshotTwo?.isEmpty 是否为真)进行用户):

Ok, I think I found the problem. When you are going through each of the documents representing users that go to a particular school, you check for every one of these users whether they are attending the event that is trying to be deleted. The problem is, for each time you do this check to see if a user is attending the event, you also do one of the following (based on whether or not the querySnapshotTwo?.isEmpty is true for that user):

  1. 如果用户正在参加该活动,则显示警告,指出该事件无法删除(因此,此事件的发生次数将与参加该活动的用户的次数相同-而不是一次),或:
  2. 如果用户不参加,它将执行删除事件操作(因此,如果多个用户未参加该事件,也会多次发生).
  1. If the user is attending the event, you display the alert saying that the event cannot be deleted (thus, this will happen for as many times as there are users that are attending that event--not just once), or:
  2. If the user is not attending the event, it performs the delete event operation (thus, this will also happen multiple times if multiple users are not attending the event).

我不确定由于 nil 值导致应用程序在 self.events.remove(at:indexPath.row)崩溃的确切原因是什么.但是我首先要确保如果您发现某个用户正在参加活动,然后您完全退出循环浏览所有用户文档,然后显示警报.然后,只有在浏览完所有个用户文档后,发现没有人参加该活动,您才执行删除操作.

I'm not sure what exactly is causing the issue with the app crashing at self.events.remove(at: indexPath.row) due to a nil value. But I would start by making sure that you completely quit looping through all the user documents if you find that a user is attending the event and then show the alert. And then you only perform the delete operation if, after going through all the user documents, you find that no one is attending the event.

棒极了,您所做的绝对是一项改进.当您找到参加事件的用户时(即当 guard querySnapshotTwo?.isEmpty == true 评估为 false 时),它没有退出delete方法的原因是您只在封包内返回 .从本质上讲,您只是退出了行: self.db.collection("student_users/\(userUUID)/events_bought").whereField("event_name"",isEqualTo:eventName).getDocuments {(querySnapshotTwo,错误)然后,您将继续 querySnapshot!.documents 中的下一个文档.

Awesome, what you've done is definitely an improvement. The reason it's not returning out of the delete method when you find a user attending the event (i.e., when guard querySnapshotTwo?.isEmpty == true evaluates to false) is because you are only returning inside the closure. Essentially, you are just returning out of the line: self.db.collection("student_users/\(userUUID)/events_bought").whereField("event_name", isEqualTo: eventName).getDocuments { (querySnapshotTwo, error) And then you will continue with the next document in querySnapshot!.documents.

因此,即使学校中的每个学生都参加了该活动,您也将始终完成for循环并继续删除该活动!

So even if every student at the school is attending the event, you will always finish the for loop and continue to delete the event anyways!

另一个不起作用的重要原因是,传递给getDocuments()调用的闭包是异步运行的.这意味着每次您进行调用时,它将安排闭包在将来的某个随机时间运行,然后立即从getDocuments()调用返回并执行for循环的下一次迭代(可能在关闭已完成).

The other important reason it does not work is because the closure that you pass into the getDocuments() call is run asynchronously. This means that each time you make the call, it will schedule the closure to be run at some random time in in the future, and then immediately return from the getDocuments() call and perform the next iteration of the for loop (likely before the closure has been completed).

要解决此问题,我相信您只需要进行两项更改:

To fix this, I believe you just need to make 2 changes:

  1. 在querySnapshot!.documents 中的文档的之前添加一个变量,以跟踪您是否找到了参加活动的学生,并将此变量更新为 true
  2. 您将需要使用 DispatchGroup ,以便仅在异步检查了每个学生之后才执行删除操作.这是一个简短的教程有关如何使用调度组的信息.在您的情况下:
    • 在for循环之前创建调度组(写 let group = DispatchGroup())
    • 在for循环内的第一行调用 group.enter()
    • 在for循环内的getDocuments()闭包内的最后一行调用 group.leave()
    • 在for循环之后立即调用以下代码:
  1. Add a variable before the for document in querySnapshot!.documents to keep track of whether or not you found a student who is attending the event, and update this variable to true inside the closure if you find a student attending the event (in place of the current ineffective return statement).
  2. You will need to use a DispatchGroup so that you only perform the delete operation once every student has been asynchronously checked. Here's a brief tutorial on how to use Dispatch Groups. In your case:
    • Create the dispatch group before the for loop (write let group = DispatchGroup())
    • Call group.enter() on the first line inside the for loop
    • Call group.leave() on the last line inside the getDocuments() closure inside the for loop
    • Call the following right after the for loop:

group.notify(queue: .main) {
  // deletion code goes here
}

这篇关于firestore文件删除并因逻辑错误而崩溃,但没有逻辑就可以删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆