读取对象
在 SwiftData 中,你可以通过多种方式读取数据,以满足不同的查询需求。本节将详细介绍两种主要的读取方式:声明式查询 (@Query 宏) 和命令式查询 (ModelContext.fetch()),以及如何使用谓词、排序描述符、数量限制和偏移量来精确控制查询结果。
使用 @Query 宏进行声明式查询
@Query 是 SwiftData 提供的一个强大的属性包装器,它允许你在 SwiftUI 视图中以声明式的方式定义数据查询。当 underlying 数据发生变化时,@Query 会自动更新视图,使得 UI 始终保持与数据同步。
import SwiftUI
import SwiftData
struct ContentView: View {
// 声明式查询,获取所有 Task 对象
@Query var tasks: [Task]
var body: some View {
List {
ForEach(tasks) { task in
Text(task.name)
}
}
}
}
带有谓词的 @Query
你可以通过传入 Predicate 参数来过滤查询结果。例如,我们只想显示已完成的任务:
import SwiftUI
import SwiftData
struct ContentView: View {
// 查询所有已完成的任务
@Query(filter: #Predicate<Task> { $0.isCompleted == true }) var completedTasks: [Task]
var body: some View {
List {
ForEach(completedTasks) { task in
Text(task.name)
}
}
}
}
带有排序描述符的 @Query
你还可以通过 sort 参数来指定查询结果的排序方式。例如,按任务创建日期降序排列:
import SwiftUI
import SwiftData
struct ContentView: View {
// 按创建日期降序查询所有任务
@Query(sort: \Task.creationDate, order: .reverse) var tasks: [Task]
var body: some View {
List {
ForEach(tasks) { task in
Text(task.name)
}
}
}
}
你也可以提供多个排序描述符,SwiftData 会按照它们在数组中的顺序进行排序。
使用 ModelContext.fetch() 进行命令式查询
除了声明式查询,SwiftData 还提供了命令式查询方式,即通过 ModelContext 的 fetch() 方法来执行更复杂的、一次性的或非视图驱动的数据检索。这在你需要在业务逻辑层或不直接与 SwiftUI 视图交互的地方获取数据时非常有用。
fetch() 方法需要一个 FetchDescriptor 对象作为参数,该对象封装了查询的所有细节。
import SwiftData
func fetchTasks(context: ModelContext) throws {
// 创建一个 FetchDescriptor,指定要查询的类型
let descriptor = FetchDescriptor<Task>()
do {
let allTasks = try context.fetch(descriptor)
print("所有任务: \(allTasks.count) 个")
} catch {
print("获取任务失败: \(error)")
}
}
谓词 (Predicate) 的使用:#Predicate
谓词是过滤查询结果的关键。SwiftData 利用 Swift 5.9 引入的 #Predicate 宏,以一种类型安全和可读性强的方式定义复杂的查询条件。
// 查询所有名称包含 "SwiftData" 且未完成的任务
let predicate = #Predicate<Task> { task in
task.name.contains("SwiftData") && task.isCompleted == false
}
var descriptor = FetchDescriptor<Task>(predicate: predicate)
let filteredTasks = try context.fetch(descriptor)
#Predicate 宏的强大之处在于,它能在编译时进行类型检查,并能被 SwiftData 运行时转换为高效的数据库查询语言(例如 SQL)。
你可以使用各种比较运算符 (==, !=, >, <, >=, <=)、逻辑运算符 (&&, ||, !) 以及字符串方法 (contains, starts(with:), ends(with:)) 来构建复杂的谓词。
排序 (Sort Descriptors)
通过 FetchDescriptor 的 sortBy 属性,你可以添加一个或多个 SortDescriptor 来指定结果的排序方式。
// 按创建日期升序,然后按名称降序排列
var descriptor = FetchDescriptor<Task>()
descriptor.sortBy = [
SortDescriptor(\.creationDate, order: .forward),
SortDescriptor(\.name, order: .reverse)
]
let sortedTasks = try context.fetch(descriptor)
限制数量与偏移 (Limit & Offset)
当你只需要部分数据或实现分页功能时,可以使用 FetchDescriptor 的 fetchLimit 和 fetchOffset 属性。
fetchLimit: 限制返回结果的数量。fetchOffset: 跳过指定数量的结果,从偏移量之后开始返回。
// 查询前 5 个任务,跳过最开始的 10 个任务
var descriptor = FetchDescriptor<Task>()
descriptor.fetchLimit = 5
descriptor.fetchOffset = 10
let paginatedTasks = try context.fetch(descriptor)
结合 fetchLimit 和 fetchOffset,你可以轻松实现分页加载功能,提高应用性能和用户体验。
通过本节的学习,你已经掌握了 SwiftData 中读取对象的多种方法,包括声明式的 @Query 和命令式的 ModelContext.fetch()。同时,你也了解了如何利用谓词、排序描述符、限制数量和偏移量来精确控制你的数据查询。在实际开发中,根据你的需求选择最合适的查询方式,将能更高效地管理和展示你的应用数据。
你现在对 SwiftData 的数据读取操作有更清晰的认识了吗?或者你希望了解如何在更复杂的场景下应用这些查询技巧?
