WWDC Quick Look 💓 By SwiftGGTeam
Meet SwiftData

Meet SwiftData

观看原视频

Highlight

SwiftData 是 Apple 为 Swift 推出的原生数据持久化框架,用 @Model 宏直接在 Swift 代码中定义数据模型,自动与 SwiftUI 集成,开发者无需编写 Core Data 的繁琐样板代码即可获得完整的增删改查和 iCloud 同步能力。

核心内容

以前要在 iOS 上做数据持久化,Core Data 几乎是唯一选择。但它的学习曲线陡峭:你需要理解 .xcdatamodeld 文件、NSManagedObject、NSPredicate、NSFetchRequest 这些概念,还要在 Swift 和 Objective-C 的 API 之间来回切换。一个简单的查询可能要写十几行代码,而且类型不安全,编译期无法检查错误。

WWDC23 推出的 SwiftData 改变了这个局面。

SwiftData 完全基于 Swift 代码,没有外部文件格式。你写一个普通的 Swift class,加上 @Model 宏,它就自动具备了持久化能力。字符串、整数、日期这些值类型直接变成数据属性,其他模型类的引用自动变成关系属性。不需要 .xcdatamodeld 文件,不需要生成代码,Schema 就是你的 Swift 代码本身。

查询也变得更简单。SwiftData 引入了原生的 PredicateFetchDescriptor,完全替代了 NSPredicate。你可以在 Xcode 里获得自动补全,编译器会检查类型错误。一个查询从原来的十几行缩减到两三行。

和 SwiftUI 的集成是开箱即用的。在 App 的 scene 上加一个 .modelContainer() modifier,在 View 里用 @Query@Environment(\.modelContext),数据就能自动绑定到 UI 上。修改模型属性,界面自动刷新;插入或删除数据,列表自动更新。

SwiftData 还内置了 CloudKit 同步、Undo/Redo、Widget 支持等平台能力。这些以前需要大量额外工作才能实现的功能,现在几乎是免费的。

数据建模

01:09)SwiftData 的核心是 @Model 宏。把它加在一个 class 上,SwiftData 就会自动分析这个类的存储属性,生成对应的持久化 Schema。

import SwiftData

@Model
class Trip {
    var name: String
    var destination: String
    var endDate: Date
    var startDate: Date
    var bucketList: [BucketListItem]? = []
    var livingAccommodation: LivingAccommodation?
}

关键点:

  • @Model 只能用于 class,因为模型需要引用语义
  • 值类型属性(String、Int、Date、struct、enum、Codable)自动成为 attributes
  • 其他模型类的引用自动成为 relationships
  • 集合类型(数组)也支持

02:46)你可以用 @Attribute@Relationship 添加元数据:

@Model
class Trip {
    @Attribute(.unique) var name: String
    var destination: String
    var endDate: Date
    var startDate: Date

    @Relationship(.cascade) var bucketList: [BucketListItem]? = []
    var livingAccommodation: LivingAccommodation?
}

关键点:

  • @Attribute(.unique) 给 name 添加唯一性约束
  • @Relationship(.cascade) 表示删除 Trip 时,关联的 bucketList 项也会被级联删除
  • @Transient 可以标记不参与持久化的属性

容器与上下文

03:43ModelContainer 是持久化后端,ModelContext 是操作数据的接口。

初始化容器有两种方式:

// 默认配置
let container = try ModelContainer([Trip.self, LivingAccommodation.self])

// 自定义配置
let container = try ModelContainer(
    for: [Trip.self, LivingAccommodation.self],
    configurations: ModelConfiguration(url: URL("path"))
)

在 SwiftUI 中,用 modifier 设置容器:

import SwiftUI

@main
struct TripsApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(
            for: [Trip.self, LivingAccommodation.self]
        )
    }
}

关键点:

  • .modelContainer() 会自动把容器注入到 SwiftUI 环境中
  • 可以配置 URL、CloudKit 容器标识符、迁移选项等
  • SwiftUI 还会自动传播 modelContext

04:20)在 View 中获取上下文:

import SwiftUI

struct ContextView: View {
    @Environment(\.modelContext) private var context
}

查询数据

05:13)SwiftData 使用原生的 Predicate 宏构建查询条件,替代了 NSPredicate

let today = Date()
let tripPredicate = #Predicate<Trip> {
    $0.destination == "New York" &&
    $0.name.contains("birthday") &&
    $0.startDate > today
}

关键点:

  • #Predicate 是 Swift 宏,支持强类型检查
  • Xcode 提供自动补全
  • 条件表达式使用原生 Swift 语法

05:32)用 FetchDescriptor 执行查询:

let descriptor = FetchDescriptor<Trip>(predicate: tripPredicate)
let trips = try context.fetch(descriptor)

05:46)添加排序:

let descriptor = FetchDescriptor<Trip>(
    sortBy: SortDescriptor(\.Trip.name),
    predicate: tripPredicate
)
let trips = try context.fetch(descriptor)

关键点:

  • FetchDescriptor 支持 predicate、sort、预取关系、结果限制等多种选项
  • SortDescriptor 支持原生 Swift 类型和 keypath

修改数据

06:15)ModelContext 提供了完整的增删改接口:

var myTrip = Trip(name: "Birthday Trip", destination: "New York")

// 插入新对象
context.insert(myTrip)

// 删除对象
context.delete(myTrip)

// 保存变更
try context.save()

关键点:

  • 创建模型对象和普通 Swift class 一样
  • insert() 后对象开始被上下文跟踪
  • 属性修改会自动被跟踪,下次 save 时提交
  • 支持 undo/redo

SwiftUI 集成

07:38@Query 属性包装器让数据绑定变得极其简单:

import SwiftUI

struct ContentView: View {
    @Query(sort: \.startDate, order: .reverse) var trips: [Trip]
    @Environment(\.modelContext) var modelContext

    var body: some View {
        NavigationStack() {
            List {
                ForEach(trips) { trip in
                    // ...
                }
            }
        }
    }
}

关键点:

  • @Query 自动从上下文中获取数据
  • 支持排序和过滤
  • 数据变化时 SwiftUI 自动刷新视图
  • SwiftData 支持新的 Observable 特性,属性变化自动触发 UI 更新

详细内容

从 Core Data 迁移

SwiftData 和 Core Data 可以共存。Apple 提供了从 Core Data 迁移到 SwiftData 的指南,包括:

  • 共享同一个持久化存储文件
  • 逐步迁移模型
  • 保持 CloudKit 同步

相关文档:Adopting SwiftData for a Core Data app

FetchDescriptor 的高级用法

FetchDescriptor 除了 predicate 和 sort,还支持:

  • 预取关系对象,减少数据库往返
  • 限制结果数量
  • 排除未保存的变更
  • 自定义获取属性

这些选项让你可以精确控制查询行为,优化性能。

平台集成

SwiftData 与多个 Apple 平台特性无缝协作:

  • CloudKit:自动 iCloud 同步,无需额外代码
  • Widgets:Widget 可以直接读取 SwiftData 数据
  • Undo/Redo:ModelContext 内置支持
  • Spotlight:可以集成 Core Spotlight 索引

核心启发

1. 用 SwiftData 重写你的笔记/待办 App

  • 做什么:把原来用 UserDefaults 或文件存储的笔记应用迁移到 SwiftData
  • 为什么值得做:获得完整的数据查询能力(按日期、标签筛选)、自动 iCloud 同步、Undo 支持
  • 怎么开始:给 Note 模型加 @Model,用 @Query 替换原来的数组,用 FetchDescriptor 实现搜索和筛选

2. 做一个支持离线缓存的阅读器 App

  • 做什么:RSS 阅读器或文章收藏 App,支持离线阅读和同步
  • 为什么值得做:SwiftData 的 CloudKit 集成让多设备同步几乎零成本,FetchDescriptor 的预取功能可以优化阅读体验
  • 怎么开始:定义 Article 模型,用 @Relationship 关联 Tag,用 predicate 实现已读/未读筛选

3. 做一个旅行规划 App(类似 Demo 中的 Trip 应用)

  • 做什么:管理旅行计划、行李清单、预算记录
  • 为什么值得做:SwiftData 的关系模型天然适合这种多实体关联的场景,SwiftUI 集成让界面开发极快
  • 怎么开始:创建 Trip、BucketListItem、LivingAccommodation 等模型,用 @Relationship(.cascade) 管理关联删除

4. 给现有 SwiftUI App 添加数据持久化

  • 做什么:把原来内存中的状态持久化到本地
  • 为什么值得做:只需加 @Model.modelContainer(),改动极小,收益很大
  • 怎么开始:识别需要持久化的数据模型,添加 @Model 宏,在 App 入口配置容器

关联 Session

评论

GitHub Issues · utterances