2025-01-19 16:45:46
,某些文章具有时效性,若有错误或已失效,请在下方留言。在SwiftData 中插入数据非常简单,默认情况下数据会自动保存,无需手动调用保存操作。以下示例演示了如何创建一个 Student
模型并插入一条记录,数据将自动保存:
@Model class Student {
var name: String
init(name: String) {
self.name = name
}
}
struct ContentView: View {
@Environment(\.modelContext) private var context
var body: some View {
Button(action: addStudent) {
Label("添加学生", systemImage: "plus")
}
}
private func addStudent() {
let newItem = Student(name: "小王")
context.insert(newItem)
}
}
如果通过 ModelConfiguration
禁用了自动保存,则需要手动调用保存操作来插入数据,下面是示例代码:
private func addStudent() {
let newItem = Student(name: "小王")
context.insert(newItem)
try? context.save()
}
多对多关系添加数据
在多对多关系模型中插入数据时,确保所有实体首先插入是非常重要的。以下示例展示了如何在修改数组之前插入实体:
let tchr = Teacher(name: "王老师", students: [])
let student1 = Student(name: "小明", teachers: [tchr])
let student2 = Student(name: "小爱", teachers: [tchr])
modelContext.insert(tchr)
modelContext.insert(student1)
modelContext.insert(student2)
tchr.students.append(student1)
tchr.students.append(student2)
或者,你也可以将 Teacher
添加到 Student
中:
student1.teachers.append(tchr)
student2.teachers.append(tchr)
支持撒消和重做
为 SwiftData 添加撤销和重做支持非常简单,实际上只需要两步:首先启用撤销功能,然后通过 SwiftUI 环境或模型上下文的撤销管理器调用撤销。首先,需要调整创建模型容器的方式,以便启用撤销功能。如果你使用的是 SwiftUl,可能需要将 modelContainer()
修饰符修改为以下代码:
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(
for: [ School.self, Student.self ],
isUndoEnabled: true
)
}
配置好撤销管理器后,你可以调用 undo()
或 redo()
方法,SwiftData 会自动撤销最近的一组更改。例如,在使用SwiftUI 时,你可以通过以下方式获取环境中的撤销管理器:
@Environment(\.undoManager) var undoManager
然后触发撤消操作,如下所示:
undoManager?.undo()
如果你是手动创建模型容器,那么你也需要手动创建撤销管理器:
container = try ModelContainer(for: Comment.self, Recipe.self)
container.mainContext.undoManager = UndoManager()
你可以使用 undoManager?.undo()
来执行撤销操作。
调用 undo()
或 redo()
会撤销或重做你最近进行的一组更改,但”一组更改”的定义比较模糊,使用时需谨慎:
1. 如果一次进行了多个更改,它们会被一起撤销。
2. 如果你进行了一次更改,调用了 save()
,然后又进行了另一项更改,两个更改都会被撤销。
你也可以直接在任何模型上下文中调用撤销管理器,但如果视图中存在其他可撤销的操作,可能会引发问题。你还可以通过以下方式重做操作:
struct ContentView: View {
@Environment(\.undoManager) var undoManager
var body: some View {
// ...
}
private func redo() {
undoManager?.redo()
}
}
撤销操作在处理插入对象和关系的删除时非常稳定,而重做操作则相对不太稳定,使用时请谨慎。
当撤销和重做功能正常运行时,你还可以通过三指滑动手势来操作:三指向左滑动撤销,向右滑动重做。此外,除了调用 undo()
和 redo()
之外,你还可以通过检查撤销管理器的 canUndo
和 canRedo
属性,判断这些操作是否可用。
不保存时回滚更改
当需要一次性对数据进行多项更改,并希望在出错时能够回滚这些更改时,ModelContext
的 transaction()
方法可能无法满足需求。正确的做法是在一个 Task
中创建一个新的模型上下文,进行所有更改后,只有在确认需要保存时才调用 save()
。如果不调用 save()
,模型上下文及其内容将自动丢弃,相当于回滚了所有更改。
例如,在批量插入用户数据时,如果发现某个用户的幸运数字是 13,可以选择不保存并退出任务,从而确保所有插入操作被取消。
Task {
let newContext = ModelContext(modelContext.container)
for i in 1...1_000 {
let number = Int.random(in: 1...10_000)
if number == 13 {
print("That's not a lucky number!")
return
}
let user = User(name: "User \(i)", luckyNumber: number)
newContext.insert(user)
}
try? newContext.save()
}
使用事务回滚
通过事务可以确保一系列操作要么全部成功,要么全部失败,从而保证数据的一致性和完整性。以下是一个示例,演示如何同时添加三条数据。在此示例中,假设进行条件检查,如果插入失败,则抛出错误,输出错误信息并回滚已插入的数据:
struct ContentView: View {
@Environment(\.modelContext) private var context
var body: some View {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
private func addItem() {
let newItems = [Student(name: "Hello1"), Student(name: "Hello2"), Student(name: "Hello3")]
do {
try context.transaction {
for item in newItems {
context.insert(item)
if item.name == "Hello2" {
throw DataError.insertionFailed("insert error...")
}
}
}
} catch DataError.insertionFailed(let message) {
print("Error: \(message)") // 输出: 插入失败
context.rollback() // ❌ 发生错误回滚
} catch {
print("Error: \(error.localizedDescription)")
context.rollback() // ❌ 发生错误回滚
}
}
}
enum DataError: Error {
case insertionFailed(String)
}
允许开发者将多条读写操作打包在一起。若中途发生错误,事务可以回滚所有更改,恢复到操作前的状态。
通常用于批量插入、更新、删除等操作,以确保数据的安全性和一致性。
暂无评论内容