繁体   English   中英

如何在不使应用程序崩溃的情况下处理 SwiftUI (iOS) 中的数据删除

[英]How to handle data deletions in SwiftUI (iOS) without crashing the app

我有一个 SwiftUI 日历应用程序,其 UI 类似于内置的 Calendar.app。 每当我尝试删除事件时,我都会崩溃。 我的应用程序的整体生命周期如下:

  • 从服务器下载日历数据并填充模型([Events]、[Users]、[Responses] 等)
  • 将源数据转换为更结构化的格式(参见https://stackoverflow.com/a/58583601/2282313
  • 呈现事件的列表视图,每个事件链接到一个详细视图和一个编辑模式(非常类似于 calendar.app)
  • 当一个事件被删除时,我告诉服务器删除该事件(如果它是重复发生的事件,服务器将删除多个事件),然后通过重新下载数据、重新填充模型并重新从服务器刷新我的数据- 生成结构化数据(这会导致列表刷新)。

当我这样做时,我会因计算值而崩溃,因为详细视图中显示的事件数据不再可用。 比如我得到一个用户的RSVP的数组索引如下:

    var responseIndex: Int {
        userData.responses.firstIndex(where: { $0.user == response.user && $0.occurrence == response.occurrence })!
    } 

我以为这是因为我在更新数据之前没有关闭显示已删除事件的视图,但即使我延迟数据刷新直到视图不再显示,我仍然会崩溃(SwiftUI 似乎将这些视图保留在记忆)。

处理数据删除的正确方法是什么? 我是否需要在我的 UserData EnvironmentObject 中保留已删除的事件并将它们标记为“已删除/隐藏”以避免此问题,或者是否有更好的方法来处理它?

这涉及到相当多的代码,所以提供一个示例很棘手,如果有人问我很乐意添加相关位。

编辑:我发现这篇文章很好地阐明了一些事情: https ://jasonzurita.com/swiftui-if-statement/

SwiftUI 非常乐意尝试渲染 nil 视图,它什么也不画。 与直觉相反,避免崩溃并使编译器满意的一个好方法是围绕此设置代码。

原始“答案”如下...

我不知道这是否是执行此操作的“正确”方法,但我最终确保我的 UserData 都没有被删除以避免崩溃。 我向我的 Occurrence(即事件)对象添加了一个“已删除”布尔值,当我刷新我的结构化数据时,我从服务器获取最新数据,但检查是否有任何旧数据不再存在。 步骤是:

  1. 从服务器获取最新的事件列表
  2. 为我的结构化数据创建第二个 init() ,它将现有数据作为参数
  3. 在新的 init() 中,展平结构化数据,根据新数据检查已删除的项目,更新未删除的数据,剔除重复项,然后合并到净新数据中。 完成后,我用修改后的数据调用我原来的 init() 来创建新的结构化数据

代码如下所示:

init(occurrences: [Occurrence], existing: [Day]) {

    // Create a mutable copy of occurrences (useful so I can delete duplicates)
    var occurrences = occurrences

    // Flatten the structured data into a plan array of occurrences again
    var existingOccurrences = existing.compactMap({ $0.occurrences }).flatMap { $0 }

    // Go through existing occurrences and see if they still exist.
    existingOccurrences = existingOccurrences.map {
        occurrence -> Occurrence in
        let occurrenceIndex: Int? = occurrences.firstIndex(where: { $0.id == occurrence.id })
        // If the occurrence no longer exists, mark it as "deleted" in the original data
        if  occurrenceIndex == nil {
            var newOccurrence = occurrence
            newOccurrence.deleted = true
            return newOccurrence
            // If it still exists, replace the existing copy with the new copy
            // (in case it has changed since the last pull from the server)
            // Remove the event from the "new" data so you don't get duplicates
        } else {
            let newOccurrence = occurrences[occurrenceIndex!]
            occurrences.remove(at: occurrenceIndex!)
            return newOccurrence
        }
    }

    // Merge the existing data (with deleted items marked) and the updated data (with deleted items removed)
    let finalOccurrences = existingOccurrences + occurrences

    // Re-initialize the strutured data with the new array of data
    self = EventData(occurrences: finalOccurrences)
}

一旦完成,我必须更新我的代码以确保我始终使用我的结构化数据作为真实来源(我以前没有这样做,因为访问“源”平面数据通常更容易,而且我'我已经在我的列表视图中更新了我的 ForEach,以便仅在 deleted 为 false 时呈现一行。

它有效,这可能是解决问题的次优方法。 但没有更多的崩溃。 仍然有兴趣听到更好的方法来解决问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM