ModelContext 是 SwiftData 框架的运行时“工作上下文”,负责追踪、注册、缓存、并持久化@Model实例的生命周期与变更,提供数据增删改查的工作区。
获取方式
ModelContext(上下文)从ModelContainer(数据容器)中获取。
1、入口文件初始化ModelContainer容器:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(try! ModelContainer(for: UserProfile.self))
}
}
}
2、SwiftUI视图通过环境值获取ModelContext:
struct ContentView: View {
@Environment(\.modelContext) private var context
var body: some View {
// 使用 context 进行数据操作
}
}
在SwiftUI视图中,ModelContext可以通过 @Environment(\.modelContext) 自动注入到视图中,在View视图中直接进行数据操作。
基本用法
1、插入数据
let newUser = User(name: "Alice", age: 25)
context.insert(newUser) // 将对象插入到上下文中
2、删除数据
context.delete(user) // 从上下文中删除对象
3、删除所有数据
try? context.delete(model: User.self)
删除某个类型的所有数据。
4、批量删除数据
try context.delete(model: PiggyBank.self, where: #Predicate { $0.isPrimary == false })
5、保存数据
try? context.save() // 提交上下文中的所有更改
6、回滚保存状态
context.rollback()
7、查询数据
let books = try context.fetch(FetchDescriptor<Book>())
for book in books {
print("Title: \(book.title), Author: \(book.author)")
}
注:通常情况下,SwiftUI使用 @Query 查询数据,这里是MdoelContext中查询数据。如果需要筛选、排序数据,还可以使用FetchDescriptor 设置数据获取方式。
8、查询数据数量
let count = try context.fetchCount(desc)
使用示例
下面是一个使用示例,SwiftUI通过环境值获取ModelCotnext,@Query获取SwiftData实例数组。
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var context
@Query var items: [Item]
var body: some View {
VStack {
List(items) { item in
Text(item.timestamp.formatted())
}
Button("删除") {
if let lastItem = items.last {
context.delete(lastItem) //删除
try? context.save() // 提交上下文中的所有更改
}
}
}
}
}
当点击删除按钮时,从上下文中删除最后一个对象,并保存。
注意事项
1、保存变更操作
数据在增删改查后,通常需要调用 context.save() 方法保存更改。
context.delete(lastItem) //删除
try? context.save() // 提交上下文中的所有更改
如果不保存,变更只存在于当前ModelContext的内存缓存中,应用退出或context重制后会丢失。特别是在ViewModel中操作时,须显式调用save()。
2、ViewModel中使用ModelContext
SwiftData的ModelContext通常通过SwiftUI的环境值注入,但是在Viewmodel或非视图场景中,无法使用环境值。
因此推荐将ModelContext从外部注入,例如在方法参数或初始化器中注入:
func createUser(_ context: ModelContext,for data: User) { ... }
3、从ModelContainer获取上下文
除了依赖SwiftUI环境,还可以从ModelContaienr实例中获取context:
let container = try ModelContainer(for: [User.self])
let context = container.mainContext // 获取主操作上下文
mainContext是ModelContainer的一个属性,与从SwiftUI中通过 @Environment(\.modelContext) 注入的上下文等价。
适用于非SwiftUI环境下(如后台任务、测试代码或工具类)构建数据访问层。
总结
SwiftData上下文环境ModelContext,是从ModelContainer中获取的。
在SwiftUI中,可以从环境值中获取ModelContainer,执行SwiftData数据的增删改查。
相关文章
1、SwiftData模型容器ModelContainer:https://fangjunyu.com/2024/11/05/swiftdata%e6%a0%b8%e5%bf%83%e7%bb%84%e4%bb%b6modelcontainer/
2、SwiftData查询描述符FetchDescriptor:https://fangjunyu.com/2025/11/20/swiftdata%e6%9f%a5%e8%af%a2%e6%8f%8f%e8%bf%b0%e7%ac%a6fetchdescriptor/
3、SwiftData查询@Query:https://fangjunyu.com/2024/11/04/swiftdata%e6%a1%86%e6%9e%b6%e5%b1%9e%e6%80%a7%e5%8c%85%e8%a3%85%e5%99%a8query/
扩展知识
1、@Query和ModelContext的关系
在SwiftUI中,可以使用@Query获取、筛选上下文的数据。
@Query var profiles: [UserProfile]
@Query实际上是从ModelContext中获取存在的数据,并自动执行查询。@Query绑定的数据会跟随ModelContext的数据变化自动更新,并重新渲染视图。
2、ModelContext完整属性与方法
1、author
表示当前上下文保存更改时的“作者标识”。用于协调保存历史、冲突解决或多来源写入系统。通常不手动设置。
2、autosaveEnabled
布尔值,指示是否允许 SwiftUI 或系统在适当时机自动调用 save。默认开启。
关闭后必须手动调用 save() 才会写入存储。
3、changedModelsArray
返回当前 context 中所有“已修改但未保存”的模型实例数组。
用于调试,或在保存前检查哪些对象发生了变更。
4、container
所属的 ModelContainer。
context 只是执行工作,container 才是模型的持久化后端(数据库、模型 schema、配置等)。
5、debugDescription
显示 context 的调试信息:包括内部状态、 pending changes、container 信息等。
常用于断点调试或日志输出。
6、deletedModelsArray
当前 context 中所有已被标记为删除、但尚未保存的模型实例。
删除之后调用 save() 才真正删除。
7、editingState
描述 context 的当前编辑状态,例如是否在事务中、是否有未保存的更改等。
通常用于内部调试,很少需要手动检查。
8、hasChanges
布尔值,表示 context 内是否存在未保存的变更(插入/修改/删除)。
等价于 Core Data 的 hasChanges。
9、insertedModelsArray
当前 context 中所有“已插入但未保存”的新模型实例。
常用于批量导入或调试复杂创建行为。
10、undoManager
关联的 UndoManager 对象。如果设置了 undoManager,则 context 的操作将自动注册撤销点。
支持系统级撤销(例如 Command+Z)。
11、delete(_ model:)
删除指定模型实例。
只是把对象标记为 deleted,直到调用 save() 才真正从数据库消失。
12、delete(model:where:)
按类型和谓词批量删除。
这是 SwiftData 的“批量删除 API”,效率高于循环 delete。
13、deleteHistory
清除模型变更历史(undo/redo 或持久化历史)。
与数据删除不同,history 只影响历史记录,不影响实体本身。
14、enumerate(_:)
遍历 context 管理的模型对象。
多用于调试或底层工具,而不是日常使用。
15、fetch(_ descriptor:)
按 FetchDescriptor 查询模型,返回对象数组。
标准查询 API。
16、fetch(descriptor:batch:)
分批次获取对象,适合大型数据量的分页或流式读取。
例如每次 1000 条降低内存压力。
17、fetchCount(_ descriptor:)
返回符合条件的对象数量(不加载实体)。
比 fetch 轻量,适合做数目统计。
18、fetchHistory
查询保存历史记录(例如是谁保存、保存内容、时间戳)。
用于调试、版本审计或同步机制。
19、fetchIdentifiers(_ descriptor:)
只返回模型的 PersistentIdentifier 列表而不加载真实对象。
适用于跨 context 通讯,例如在通知中收到 ID 后再 fetch 实体。
20、fetchIdentifiers(_ descriptor:batchSize:)
按批次加载模型对象,(每批为 batchSize 个),适合分批处理大量实体而不一次性把所有对象都放内存。
21、insert(_ model:)
把模型实例注册到 context。
未保存前,此对象存在于 context 的 working set,不写入数据库。
22、model(for:)
根据 PersistentIdentifier 返回对应的模型实例(若在 context 中已加载)。
如果未加载,则返回 nil。
用于“定位”当前 context 是否持有某对象。
23、processPendingChanges
强制 context 立即处理挂起的更改(relationship 同步、undo 记录、事件发布)。
不等价于 save,不会写入数据库。
在关系更新、级联删除、调试时很常用。
24、registeredModel(for:)
返回 context 内部已经注册的模型对象(已加载或已 attach 的实例)。
即使没有从数据库 fetch,也可能存在 registered 状态。
用于判断某个对象是否属于当前 context,避免对非托管对象操作导致删除失败。
25、rollback
撤销所有未保存的更改,将 context 恢复到上一次 save 的状态。
等价于 Core Data 的 rollback。
26、save
将插入、修改、删除全部写入数据库。
保存后,pending changes 清空,模型状态变为 clean。
27、transaction / transaction(block:)
开启或执行一次事务操作(多个数据库变更视为单一原子性操作)。
transaction(block:) 是最常用的形式:
try context.transaction {
context.insert(a)
context.delete(b)
}
事务成功会自动保存;若 block 抛错则全部回滚。
