SwiftData上下文环境ModelContext
SwiftData上下文环境ModelContext

SwiftData上下文环境ModelContext

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 抛错则全部回滚。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注