在 SwiftData(Apple 最新的数据持久化框架)中,可以通过 Filtering(过滤)和 Sorting(排序)对数据进行查询和组织。这些操作在 SwiftData 的 @Query
属性包装器中得到了优雅且易用的处理。
以下我将通过一些常用的过滤和排序操作演示如何使用 SwiftData 有效地管理数据。
前置理解 #
SwiftData 的核心组件:
Model
: 数据模型使用@Model
注解来定义。@Query
: 一个强大的属性包装器,用于通过 SwiftData 管理和查询数据。Predicate 和 SortDescriptor
: 在过滤和排序中,这些对象用于定义查询条件和排序规则。
过滤和排序的目标:
- Filtering:通过条件限制查询结果,例如查找指定属性值、范围或符合谓词的数据。
- Sorting:以特定的顺序排序查询结果,例如升序、降序或自定义比较逻辑。
1. 定义 @Model
数据模型
#
在 SwiftData 中,所有数据模型都需要用 @Model
注解定义。以下我们定义一个简单的任务模型:
import SwiftData
@Model
class Task {
var id: UUID
var title: String
var isCompleted: Bool
var dueDate: Date
init(title: String, isCompleted: Bool, dueDate: Date) {
self.id = UUID()
self.title = title
self.isCompleted = isCompleted
self.dueDate = dueDate
}
}
说明:
Task
模型代表一个任务,包含以下属性:id
: 唯一标识符。title
: 任务标题。isCompleted
: 是否已完成。dueDate
: 任务截止日期。
之后我们会对这些任务数据进行 过滤 和 排序。
2. Filtering 的例子 #
例子 1: 基于布尔属性的过滤 #
从数据中筛选出所有未完成的任务:
@Query(filter: #Predicate { $0.isCompleted == false })
private var pendingTasks: [Task]
- 用法:
- 使用
#Predicate
构建过滤条件。 $0.isCompleted == false
表示过滤出isCompleted
属性为false
的数据。
- 使用
例子 2: 按字符串部分匹配过滤(模糊搜索) #
筛选出任务标题包含关键字 “important” 的任务:
@Query(filter: #Predicate { $0.title.contains("important") })
private var importantTasks: [Task]
- 说明:
- 使用字符串方法
.contains()
进行部分匹配。 - 确保模糊搜索对大小写敏感。如果需要大小写不敏感,请使用
.localizedCaseInsensitiveContains()
。
- 使用字符串方法
例子 3: 基于时间范围过滤 #
筛选出未来 7 天到期的任务:
@Query(filter: #Predicate {
let nextWeek = Calendar.current.date(byAdding: .day, value: 7, to: Date())
return $0.dueDate <= nextWeek && $0.dueDate >= Date()
})
private var tasksDueNextWeek: [Task]
- 说明:
- 使用
Calendar
和Date
计算未来 7 天的日期范围。 - 通过日期比较
<=
和>=
过滤符合条件的任务。
- 使用
例子 4: 组合多个条件 #
筛选出未来任务(未完成任务且在一周内到期):
@Query(filter: #Predicate {
let nextWeek = Calendar.current.date(byAdding: .day, value: 7, to: Date())
return $0.isCompleted == false && $0.dueDate <= nextWeek
})
private var futureTasks: [Task]
- 组合条件:通过
&&
或||
实现多个条件的组合。
例子 5: 动态过滤 #
可以根据用户输入动态调整条件,比如通过任务标题关键字搜索:
struct SearchView: View {
@State private var searchText = ""
@Query var filteredTasks: [Task]
var body: some View {
VStack {
TextField("Search tasks", text: $searchText)
.onChange(of: searchText) { text in
$filteredTasks = Query(Task.self, filter: #Predicate {
$0.title.contains(text)
})
}
List(filteredTasks) { task in
Text(task.title)
}
}
}
}
- 使用动态条件:
- 动态更新
@Query
的filter
。 - 基于用户输入的
searchText
创建过滤条件。
- 动态更新
3. Sorting 的例子 #
例子 1: 基于单个属性排序 #
将任务按照截止日期升序排列:
@Query(sort: #SortDescriptor(\.dueDate))
private var tasksSortedByDueDate: [Task]
- 说明:
- 使用
SortDescriptor
指定排序规则。 \.dueDate
表示对dueDate
属性进行排序。
- 使用
例子 2: 降序排列 #
将任务按截止日期降序排列:
@Query(sort: #SortDescriptor(\.dueDate, order: .reverse))
private var recentTasks: [Task]
- 用法:
- 在
#SortDescriptor
中设置order: .reverse
,将排序规则切换为降序。
- 在
例子 3: 多重排序 #
如果需要按多个属性排序,可以使用多个 SortDescriptor
:
@Query(sort: [
#SortDescriptor(\.isCompleted), // 未完成的任务优先
#SortDescriptor(\.dueDate) // 截止日期升序排列
])
private var prioritizedTasks: [Task]
- 说明:
- 第一排序规则:
isCompleted
,优先排列未完成任务。 - 第二排序规则:
dueDate
,相同完成状态时按截止日期升序排列。
- 第一排序规则:
例子 4: 动态排序 #
结合用户选择,动态切换不同的排序规则:
struct TasksView: View {
@State private var sortOption: String = "Due Date"
@Query var sortedTasks: [Task]
var body: some View {
Picker("Sort by", selection: $sortOption) {
Text("Due Date").tag("Due Date")
Text("Title").tag("Title")
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: sortOption) { option in
$sortedTasks = Query(
Task.self,
sort: option == "Due Date" ? #SortDescriptor(\.dueDate) : #SortDescriptor(\.title)
)
}
List(sortedTasks) { task in
Text(task.title)
}
}
}
- 说明:
- 动态更新排序规则。
- 使用用户选择控制
@Query
的排序方式。
4. Filtering 和 Sorting 结合使用 #
多条件过滤与排序可以联合使用。例如,筛选未完成的任务并按截止日期升序排列:
@Query(
filter: #Predicate { $0.isCompleted == false },
sort: #SortDescriptor(\.dueDate)
)
private var prioritizedIncompleteTasks: [Task]
5. 自定义方式通过代码查询 #
在某些特定情况下,你可能需要通过编程方式(非属性包装器)直接查询和使用过滤与排序。
func fetchIncompleteTasks(in context: ModelContext) -> [Task] {
do {
let results = try context.fetch(Query(
filter: #Predicate { $0.isCompleted == false },
sort: #SortDescriptor(\.dueDate)
))
return results
} catch {
print("Error fetching tasks: \(error)")
return []
}
}
总结 #
- SwiftData 使用
@Query
简化了常见的 Filtering 和 Sorting 操作。 - 过滤(Filtering) 是通过
#Predicate
定义条件实现,比如部分匹配或日期范围。 - 排序(Sorting) 是通过
#SortDescriptor
定义规则控制顺序,可支持单一或多重排序。 - 结合动态过滤和排序,开发者可以实现更灵活的、用户可控的数据查询功能。
通过这些示例,您可以轻松构建复杂的业务逻辑。