Highlight
SwiftData 通过
@Model宏、@Query属性包装器和.modelContainer()修饰符,让 SwiftUI 应用的数据持久化只需要几行代码,同时自动获得 Observable 能力、自动保存和文档型应用支持。
核心内容
从内存到持久化:一步之遥
以前,给 SwiftUI 应用添加数据持久化需要引入 Core Data,学习复杂的对象图管理、上下文保存和迁移规则。很多开发者因此放弃持久化,数据只在内存中存在,应用关闭就丢失。
SwiftData 改变了这个局面。它把数据持久化变得和声明 UI 一样简单。
(03:33)给现有模型类加上 @Model 宏,它就变成可持久化的 SwiftData 模型。同时自动获得 Observable 协议的一致性,不需要 @Published,不需要 ObservableObject。
@Query 自动查询和刷新
(05:52)用 @Query 替换 @State,视图就能直接查询 SwiftData 存储中的模型。数据变化时视图自动刷新,和 @State 的行为一致。
ModelContainer 配置存储栈
(08:27)通过 .modelContainer() 修饰符在视图层级中配置存储栈。WindowGroup 级别的 container 被该组创建的所有窗口共享。也可以给单个视图配置独立的 container。
自动保存
(11:13)SwiftData 在 UI 事件和用户输入时自动保存 model context,不需要手动调用 save()。只有在需要立即持久化的场景(如分享数据前)才需要显式调用。
文档型应用
(13:34)SwiftUI 支持 SwiftData 驱动的文档型应用。用 DocumentGroup 初始化器指定模型类型和内容类型,SwiftData 自动处理每个文档的存储。用户获得标准的文档操作:新建、打开、保存、分享。
详细内容
定义 SwiftData 模型
(03:33)闪卡应用的 Card 模型改造:
import SwiftData
@Model
final class Card {
var front: String
var back: String
var creationDate: Date
init(front: String, back: String, creationDate: Date = .now) {
self.front = front
self.back = back
self.creationDate = creationDate
}
}
关键点:
import SwiftData引入框架@Model宏让类可持久化- 不需要
ObservableObject,不需要@Published @Model自动添加 Observable 协议一致性- 存储属性自动成为可观察属性
绑定到 SwiftData 模型
(04:25)编辑视图用 @Bindable 创建绑定:
struct CardEditorView: View {
@Bindable var card: Card
var body: some View {
Form {
TextField("Front", text: $card.front)
TextField("Back", text: $card.back)
}
}
}
关键点:
@Bindable是最轻量的绑定包装器$card.front生成Binding<String>TextField读取和写入绑定值- 不需要
@ObservedObject
查询模型
(05:52)内容视图用 @Query 获取数据:
struct ContentView: View {
@Query private var cards: [Card]
var body: some View {
List(cards) { card in
CardRow(card: card)
}
}
}
关键点:
@Query自动从 SwiftData 存储查询数据- 数据变化时视图自动刷新
- 支持排序、过滤和动画配置
- 使用视图继承的 model context 作为数据源
带排序的查询:
@Query(sort: \Card.creationDate, order: .reverse) private var cards: [Card]
关键点:
sort参数指定排序键路径order: .reverse按降序排列- 也支持多字段排序和过滤条件
设置 ModelContainer
(08:27)在 App 定义中配置:
@main
struct FlashCardApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: Card.self)
}
}
关键点:
.modelContainer(for:)为 WindowGroup 设置存储栈- 该组创建的所有窗口共享同一个 container
- 子视图只能操作 container 中声明的模型类型
- 没有配置 container 的视图无法使用 SwiftData
为 Preview 提供样本数据
(09:24)创建内存中的 container 填充预览数据:
import SwiftData
@MainActor
let previewContainer: ModelContainer = {
let schema = Schema([Card.self])
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
let container = try! ModelContainer(for: schema, configurations: config)
let sampleCards = [
Card(front: "Who invented the compiler?", back: "Grace Hopper"),
Card(front: "What is SwiftData?", back: "A persistence framework")
]
for card in sampleCards {
container.mainContext.insert(card)
}
return container
}()
#Preview {
ContentView()
.frame(minWidth: 500, minHeight: 500)
.modelContainer(previewContainer)
}
关键点:
isStoredInMemoryOnly: true创建内存数据库- 在 Preview 中用
.modelContainer(previewContainer)注入 - 预览中显示真实数据,方便调试 UI
@MainActor确保在主线程创建
创建和保存模型
(10:30)通过 model context 插入新数据:
struct CardListView: View {
@Environment(\.modelContext) private var modelContext
@Query private var cards: [Card]
var body: some View {
List(cards) { card in
Text(card.front)
}
.toolbar {
Button("Add", systemImage: "plus") {
let newCard = Card(front: "Sample Front", back: "Sample Back")
modelContext.insert(newCard)
}
}
}
}
关键点:
@Environment(\.modelContext)获取视图的 model contextmodelContext.insert()把新模型加入上下文- SwiftData 自动保存,不需要手动调用
save() - 自动保存由 UI 事件和用户输入触发
- 只有在需要立即持久化时才显式调用
save()
文档型应用
(13:34)用 SwiftData 做存储的文档应用:
@main
struct SwiftDataFlashCardSample: App {
var body: some Scene {
#if os(iOS) || os(macOS)
DocumentGroup(editing: Card.self, contentType: .flashCards) {
ContentView()
}
#else
WindowGroup {
ContentView()
.modelContainer(for: Card.self)
}
#endif
}
}
关键点:
DocumentGroup(editing:contentType:)指定模型类型和内容类型- SwiftData 自动为每个文档创建独立的 model container
- 不需要手动设置
.modelContainer() - 获得标准的文档操作:新建、打开、保存、分享
定义内容类型:
import UniformTypeIdentifiers
extension UTType {
static var flashCards: UTType {
UTType(exportedAs: "com.example.flashcards")
}
}
在 Info.plist 中声明:
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>com.example.flashcards</string>
<key>UTTypeDescription</key>
<string>Flash Cards Deck</string>
<key>UTTypeConformsTo</key>
<array>
<string>com.apple.package</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>sampledeck</string>
</dict>
</dict>
</array>
关键点:
- 内容类型标识符要和代码中的一致
- SwiftData 文档是 package 类型
- 指定文件扩展名帮助系统识别
com.apple.package表示目录结构的文档
核心启发
-
把现有 SwiftUI 应用加上数据持久化
- 给模型类加
@Model宏,替换@State为@Query - 在 App 定义中加
.modelContainer(for:) - 不需要重写数据层,改动量极小
- 入口:
@Model+@Query+.modelContainer()
- 给模型类加
-
做一个跨平台的笔记应用
- 用 SwiftData 存储笔记,支持 iOS、macOS、watchOS、tvOS
- 用
@Query的排序和过滤功能实现文件夹和标签 - 入口:
@Query(sort: \Note.creationDate)+\Note.folder
-
构建文档型创作工具
- 用
DocumentGroup让每个文档是独立的 SwiftData 存储 - 用户可以在不同窗口打开不同文档
- 文档可以直接分享和同步
- 入口:
DocumentGroup(editing: Model.self, contentType: .customType)
- 用
-
为 Preview 创建可复用的样本数据容器
- 创建
previewContainer单例,填充各种测试数据 - 所有 Preview 共享同一个内存数据库
- 入口:
ModelConfiguration(isStoredInMemoryOnly: true)
- 创建
关联 Session
- What’s new in SwiftUI — SwiftUI 2023 全部新特性概览
- Discover Observation in SwiftUI — @Observable 宏的工作原理
- Meet SwiftData — SwiftData 框架入门介绍
- Build programmatic UI with Xcode Previews — 用 Xcode Previews 加速开发
评论
GitHub Issues · utterances