
概述
从 WWDC 23 开始,苹果推出了崭新的数据库框架 SwiftData。而在今年的 WWDC 24 中苹果再接再厉,为 SwiftData 2.0 加入了全新的历史记录追踪(History Trace)、"墓碑(Tombstone)"等诸多激动人心的新功能。

那么,究竟什么是 History Trace?我们又该如何驾驭它呢?
在本篇博文中,您将学到如下内容:
- SwiftData 2.0 中全新的 History Trace 机制如何改变游戏规则?
- 如何实时监听 SwiftData 持久存储中数据的变化?
这是本系列第三篇博文。闲言少叙,让我们马上开始 SwiftData 精彩的探究之旅吧!
Let's dive in!!!;)
5. SwiftData 2.0 中全新的 History Trace 机制如何改变游戏规则?
在今年的 WWDC 24 中,苹果推出了 SwiftData 2.0。新版本 SwiftData 在增强原有功能的基础之上还添加了一系列全新的特性,其中就包括 History Trace。
历史记录追踪(History Trace)机制专门用来查询 SwiftData 模型数据更改的历史记录,主要来说它有以下几种用途:
- 了解数据存储何时发生了更改?发生了什么更改?即使记录从数据库中被彻底删除之后仍然可以获取其部分信息("墓碑"机制);
- 了解如何使用该信息构建远程服务器同步;
- 处理进程外的更改事件;
History Trace "雪中送炭"的一个用处是同步 App 和 Widgets 间的数据变化,这在 SwiftData 2.0 之前我们需要用比较复杂的手段才可以完成,而用历史跟踪则会非常简单。
那么有的小伙伴可能要问了:History Trace 对于 SwiftData 后台数据更改与界面之间的同步又有什么用呢?
其实,History Trace 的核心是监听持久存储上数据的改变,而我们 App 中不同模型上下文都对应着同一个底层存储,这意味着:我们可以在主上下文中利用 History Trace 捕捉到其它私有上下文对底层数据修改的"蛛丝马迹"!
注意:History Trace 只存在于 SwiftData 2.0 以后的框架中,这意味只有在不低于 iOS 18+、watchOS 11+ 以及 macOS 15+ 等这些系统上才可以使用它。
而且更妙的是,我们不再需要"囫囵吞枣"似得刷新整个视图,只需刷新变化对象所对应的那个视图即可。
不过,首先我们必须先能够知道 SwiftData 底层数据究竟在何时发生了改变。
6. 如何实时监听 SwiftData 持久存储中数据的变化?
众所周知,SwiftData 是建立在 CoreData 基础之上的。这意味着在运行时它必然隐藏了 CoreData "活着的灵魂"。
在 CoreData 中我们可以通过 NSPersistentStoreRemoteChange 消息来监听本地存储是否发生改变,这同样也可以适用于 SwiftData。

所不同的是:现在我们无法细粒度捕获某个特定 NSPersistentStoreCoordinator 上的变化,只能全盘"照单全收"。不过,这并不影响大局。
我们只需在之前 ContentView 视图的代码上"略施小计",即可捕获后台线程中私有上下文里数据的更改:
swift
struct ContentView: View {
@Environment(\.modelContext) var modelContext
@Query var items: [Item]
@State var refreshID = false
var body: some View {
NavigationStack {
VStack {
List(items) { item in
Text(item.name).font(.headline.weight(.heavy))
}
.id(refreshID)
}
.toolbar {
ToolbarItem(placement:.topBarTrailing) {
Button("New", systemImage: "plus.app") {
Task.detached {
let modelContext = ModelContext(.preview)
let item = Item(name: "\(Int.random(in: 0...10000))")
modelContext.insert(item)
try! modelContext.save()
await MainActor.run {
refreshID.toggle()
}
}
}
.foregroundStyle(.white)
.tint(.green)
}
}
}
.onReceive(NotificationCenter.default.publisher(for: .NSPersistentStoreRemoteChange).receive(on: DispatchQueue.main)) { _ in
print("DB changed!")
}
}
}
从上面的代码能够看到,我们在视图里使用 onReceive 修改器方法在主线程中稳妥的监听了 NSPersistentStoreRemoteChange 消息。
在 Xcode 预览中运行看一下效果:

如您所愿,现在我们已经可以顺利捕获到 SwiftData 本地持久存储上的变化啦!
不过,目前我们仍然是通过强制刷新整个视图来触发后台私有上下文新增数据显示的。
别急,利用上面介绍的 History Trace 机制,我们在下一篇博文将尝试来重构它。
总结
在本篇博文中,我们介绍了 SwiftData 2.0 中新引入的历史记录追踪(History Trace)机制;我们还讨论了如何在 SwiftUI 中实时监听本地持久存储中数据的变化。
在下一篇博文中,我们将会把 History Trace 应用在实际的代码中以解决后台线程与 App 界面间数据同步的问题,敬请期待吧!
感谢观赏,再会!8-)