由一个 SwiftData “诡异”运行时崩溃而引发的钩深索隐(三)

概述

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

那么,究竟什么是 History Trace?我们又该如何驾驭它呢?

在本篇博文中,您将学到如下内容:

  1. SwiftData 2.0 中全新的 History Trace 机制如何改变游戏规则?
  2. 如何实时监听 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-)

相关推荐
博睿谷IT99_39 分钟前
GaussDB 和 openGauss 怎么区分?
数据库·华为认证·opengauss·it·gaussdb
野犬寒鸦1 小时前
今日面试之快问快答:Redis篇
java·数据库·redis·后端·缓存·面试·职场和发展
思茂信息2 小时前
CST微波混频电路 --- 频线任务,谐波平衡(Harmonic Balance)
数据库·3d·负载均衡·软件工程·cst·电磁仿真
HarderCoder2 小时前
深入理解 SwiftUI 中的 `@Observable` 与 `@Bindable`:从原理到实践
swiftui·swift
lang201509282 小时前
MySQL InnoDB压缩:OLTP性能优化实战
数据库·mysql
roman_日积跬步-终至千里3 小时前
【软件架构设计(40)】数据库规范化与性能优化
数据库·oracle·性能优化
一个天蝎座 白勺 程序猿3 小时前
Oracle与Kingbase深度兼容体验:从连接配置到性能优化全解析
数据库·oracle·性能优化·kingbase·金仓数据库
lang201509283 小时前
InnoDB调优指南:性能优化全解析
数据库·mysql
他们叫我技术总监3 小时前
帆软Report11多语言开发避坑:法语特殊引号导致SQL报错的解决方案
java·数据库·sql
UNbuff_03 小时前
MySQL所有关键字详细含义说明
数据库·mysql