在我的应用程序中,我需要首次打开时将一些数据预加载到应用程序中,并允许用户在应用程序内恢复该数据(通过按钮)。例如,我有一个 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 会愉快地保存添加了默认项目的上下文。但是,当用户通过按钮触发容器重置时,它会崩溃并出现上述错误。
您正在
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()
}