为什么使用 viewContext 创建一个新的 CloudKit 项目会无限刷新视图?

Why does creating a new CloudKit item with viewContext infinitely refresh views?

我正在使用 CloudKit 中托管的存储数据创建一个应用程序。当我对任何这些列表项执行正常滑动以删除操作时, deleteAlert() 显示(应该显示)。但是,只要显示警报,代码就会不断循环并创建无限数量的空白 Category 值,并将它们添加到列表中。同时,警报不允许您正常点击任何按钮,但如果您在按钮上滑动手指,您会感觉到很多短的触觉反馈脉冲(我怀疑它也在循环创建许多重叠警报)。

import SwiftUI

struct CategoryListView: View {
    
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(entity: Category.entity(), sortDescriptors: [], animation: .default)
    private var categories: FetchedResults<Category>
    
    // Passed value
    var accountSelection: String
    
    @State private var deletingItem = false
    @State private var deleteIndexSet: IndexSet?
    @State private var showingAddView = false
        
    var body: some View {
        
        List {

            ForEach(categories) { category in
                HStack {
                    Button(action: {
                        self.showingAddView.toggle()
                    }) {
                        Text("\(category.name ?? "")")
                    }
                }
                    .alert(isPresented: $deletingItem, content: deleteAlert)
            }
                .onDelete { indexSet in
                    self.deletingItem = true
                    self.deleteIndexSet = indexSet
                }
        }
    }

    func deleteAlert() -> Alert {
        
        var deletedCategory = Category(context: viewContext) // removing this line causes everything to work properly

        try! deletedCategory = categories[deleteIndexSet?.first ?? 0]
        return Alert(
            title:           Text("Delete \(deletedCategory.name ?? "nil")?"),
            message:         Text("Deleting \(deletedCategory.name ?? "nil") will not remove all entries from that category."), // TODO: make it so that if entry does not have a category, add it to a "miscellaneous" or "other" category
            primaryButton:   .cancel(),
            secondaryButton: .destructive(Text("Delete"), action: {print("")})
        )
    }
}

视图不断刷新的原因归结为 @FetchRequest 的工作方式(CloudKit 获取项目的数组)。每当 categories 的值发生变化时(如 @State@ObservedObject 等),它所附加的视图就会刷新。在这种情况下,以下循环将无限重复:

  1. deleteAlert() 内部,var deletedCategory = Category(context: viewContext) 创建一个新的 Category 并立即将其添加到当前上下文(列表)。

  2. 这会导致视图如前所述进行刷新。

  3. $deletingItem 的值仍然为真,因此将显示另一个警报。

  4. 显示警报时,会触发 deleteAlert().

    中的代码
  5. 冲洗并无限次重复步骤 1-4。

TL;DR 不要在 View/Body 中创建 ManagedObjects/ObservableObjects(正如@lorem ipsum 也指出的那样)。