由一个 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-)

相关推荐
在努力的前端小白2 分钟前
Spring Boot 敏感词过滤组件实现:基于DFA算法的高效敏感词检测与替换
java·数据库·spring boot·文本处理·敏感词过滤·dfa算法·组件开发
未来之窗软件服务4 分钟前
自建知识库,向量数据库 (九)之 量化前奏分词服务——仙盟创梦IDE
数据库·仙盟创梦ide·东方仙盟·自建ai·ai分词
麦兜*1 小时前
Swift + Xcode 开发环境搭建终极指南
开发语言·ios·swiftui·xcode·swift·苹果vision pro·swift5.6.3
冒泡的肥皂3 小时前
MVCC初学demo(一
数据库·后端·mysql
.Shu.4 小时前
Redis Reactor 模型详解【基本架构、事件循环机制、结合源码详细追踪读写请求从客户端连接到命令执行的完整流程】
数据库·redis·架构
薛晓刚7 小时前
当MySQL的int不够用了
数据库
SelectDB技术团队7 小时前
Apache Doris 在菜鸟的大规模湖仓业务场景落地实践
数据库·数据仓库·数据分析·apache doris·菜鸟技术
星空下的曙光8 小时前
mysql 命令语法操作篇 数据库约束有哪些 怎么使用
数据库·mysql
小楓12018 小时前
MySQL數據庫開發教學(一) 基本架構
数据库·后端·mysql
染落林间色8 小时前
达梦数据库-实时主备集群部署详解(附图文)手工搭建一主一备数据守护集群DW
数据库·sql