插入项目后保存 SwiftData 上下文时出错:线程 1:EXC_BREAKPOINT

问题描述 投票:0回答:1

在我的应用程序中,我需要首次打开时将一些数据预加载到应用程序中,并允许用户在应用程序内恢复该数据(通过按钮)。例如,我有一个 ContentView,其中包含 MyModel 项目列表,以及一个用于恢复默认项目的工具栏按钮。

我的型号:

@Model
class MyModel {
    var title: String
    init(title: String) {
        self.title = title
    }
    
    static let defaults = [
        MyModel(title: "Pork"),
        MyModel(title: "Chicken"),
        MyModel(title: "Lamb"),
        MyModel(title: "Shortbread"),
    ]
}

由于我们需要在不同的地方访问模型的容器,并对其调用特殊方法,因此我创建了一个特殊的类来保存它,称为

ContainerManager
。它有一个
shared
实例,并在内部初始化时存储上下文和容器,此外,还使用默认的
MyModel
项目填充容器。

容器管理器:

@MainActor
class ChipContainerManager {
    var container: ModelContainer
    var context: ModelContext
    
    private init() {
        container = try! ModelContainer(for: MyModel.self)
        context = ModelContext(container)
        
        if containerIsEmpty() {
            addDefaultChips()
        }
    }
    
    static let shared = ChipContainerManager()
    
    
    func containerIsEmpty() -> Bool {
        do {
            let chipFetch = FetchDescriptor<MyModel>()
            return try container.mainContext.fetch(chipFetch).isEmpty
        } catch {
            fatalError("Failed to check if container is empty: \(error)")
        }
    }
    
    
    func restContainerToDefaults() {
        try! container.erase()
        addDefaultChips()
    }
    
    
    func addDefaultChips() {
        MyModel.defaults.forEach { chip in
            container.mainContext.insert(chip)
        }
        
        do {
            try container.mainContext.save()
        } catch {
            print("Error saving context after adding default chips: \(error)")
        }
    }
}

在我看来,我访问项目列表,允许用户删除它们或添加新项目,并通过调用 ContainerManager 方法将容器重置回其默认状态。

内容查看和入口

@main
struct iOS_AppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(ChipContainerManager.shared.container)
                .modelContext(ChipContainerManager.shared.context)
        }
    }
}

struct ContentView: View {
    @Query var models: [MyModel]
    @Environment(\.modelContext) private var context
    
    var body: some View {
        NavigationStack {
            List(models) { m in
                HStack {
                    Text(m.title)
                    Button("Delete", systemImage: "trash") {
                        context.delete(m)
                    }
                }
            }
            .toolbar {
                ToolbarItem {
                    Button("Reset", action: ChipContainerManager.shared.restContainerToDefaults)
                }
                ToolbarItem {
                    Button("New item") {
                        context.insert(MyModel(title: "I added this one"))
                    }
                }
            }
        }
    }
}

问题:

首次运行应用程序时,SwiftData 会愉快地保存添加了默认项目的上下文。但是,当用户通过按钮触发容器重置时,它会崩溃并出现上述错误。

录音:

swift swiftui swiftdata
1个回答
0
投票

您正在

ModelContainer
上使用名为
erase()
的全新函数,并查看文档,因为该页面是空的,所以它没有告诉我们任何信息。

当我在启用调试日志记录的情况下运行代码时,当调用

erase()
时,我会看到两条消息打印到控制台:

CoreData:注释:与sqlite数据库断开连接。

错误:无法删除存储的支持目录:/Users/.../Library/Developer/CoreSimulator/Devices/.../Library/Application Support/default.store

所以这对我来说意味着“erase()”函数所做的不仅仅是删除持久化的对象,而且无论它试图做什么也没有完全成功。

总而言之,这看起来像是一个不在这里使用的函数(在我们有一些文档/解释它应该做什么之前可能根本不使用。

您想要的解决方案是从

ModelContext

中删除

最快的方法是使用

delete(model:)
但它需要您手动更新 UI

func restContainerToDefaults() {
    try? container.mainContext.delete(model: MyModel.self)
    try? container.mainContext.save()
    addDefaultChips()
}

另一种选择是获取并删除所有对象,这在正确更新 UI 方面会表现得更好

func restContainerToDefaults() {
    for model in (try? container.mainContext.fetch(FetchDescriptor<MyModel>())) ?? [] {
        container.mainContext.delete(model)
    }
    try? container.mainContext.save()
    addDefaultChips()
}
© www.soinside.com 2019 - 2024. All rights reserved.