WWDC23 初识 SwiftData
SwiftData 是 WWDC 2023 发布的一个功能强大的数据建模、持久化管理框架,Swift 原生代码,无需引入额外文件(如 Core Data 的 data model 描述文件)。无缝接入 SwiftUI 声明式语法,实现建模,执行查找、过滤操作。这一切都得益于新的 Swift Macros 特性。
此外 SwiftData 结合 DocumentGroup
可以很好地支持文档类型的应用,参考示例项目 https://developer.apple.com/documentation/SwiftUI/Building-a-document-based-app-using-SwiftData。
使用 model 宏建模
@Model
是新的 Swift 宏,用来定义模型 schema。同时 Swift Data 还提供了丰富的接口用于定义模型属性、关系等。
import SwiftData
// 声明模型类,@Model 会自动创建 schema
@Model
class Trip {
// 自定义 attribute
@Attribute(.unique) var name: String
var destination: String
var endDate: Date
var startDate: Date
// 自定义关系
@Relationship(.cascade) var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
模型属性支持基本的值类型,如 string、int、float 类型,同时也支持其他复杂的类型如 struct、enum 类型(需要其符合 Codable
协议)以及集合类型。如果不想为某一个属性建模,可以使用 @Transient
。
Container
模型容器为应用提供了持久化支持,可以参考 Core Data 栈中的相关概念。同样的,Cotainer 可以使用默认设置,也可以自定义如 URL、CloudKit、group container identifiers 和迁移选项等。设置好 container 就可以执行 CRUD 操作了。
设置 Container
import SwiftData
// 最简单的方式,只用 schema 初始化
let container = try ModelContainer([Trip.self, LivingAccommodation.self])
// 或者,设置 schema ,同时进行一些自定义
let container = try ModelContainer(
for: [Trip.self, LivingAccommodation.self],
configurations: ModelConfiguration(url: URL("path"))
)
使用 SwiftUI 修改器,注入 container
import SwiftUI
@main
struct TripsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(
for: [Trip.self, LivingAccommodation.self]
)
}
}
访问 model context
Model context 用于执行一系列数据操作:
- 跟踪修改
- 获取数据
- 保存修改
- 撤销修改
在 SwiftUI 中设置好 container 后,使用视图环境变量访问 model context:
import SwiftUI
struct ContextView : View {
@Environment(\.modelContext) private var context
}
如果不是在视图层访问模型上下文,可以直接使用 container 实例化:ModelContext(container)
访问数据
Swift 提供了新的原生类型,Predicate
、FetchDescriptor
,以及改进的 SortDescriptor
#Predicate
宏创建查询谓词
let today = Date()
let tripPredicate = #Predicate<Trip> {
$0.destination == "New York" &&
$0.name.contains("birthday") &&
$0.startDate > today
}
查询
let descriptor = FetchDescriptor<Trip>(
// Sort
sortBy: SortDescriptor(\Trip.name),
predicate: tripPredicate
)
// Fetch
let trips = try context.fetch(descriptor)
使用 Model Context 管理数据
var myTrip = Trip(name: "Birthday Trip", destination: "New York")
// 插入数据
context.insert(myTrip)
// 删除数据
context.delete(myTrip)
// 手动保存更改
try context.save()
注意,当通过视图修改或用户输入数据后,SwiftData 会自动保存,所以
context.save()
不是必需的。只有一些特殊情况,即你需要确保数据已经保存好的时候,如你需要从自己的应用分享刚刚编辑的内容,才需要显式的调用该方法。
@Query
使用 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
// ...
}
}
}
}
}
本文基于 WWDC23 Meet SwiftData 完成 https://developer.apple.com/videos/play/wwdc2023/10187/