置顶
菜鸟入门笔记,如有谬误之处还请大佬指出
深耕细作 笃行致远
相关文章
- 《SwiftUI入门 - Core Data初探与实践》
- 《SwiftUI入门 - Core Data包装器@FetchRequest的排序、筛选和分页》
- 《SwiftUI入门 - Core Data数据的分组、聚合和汇总》
- 《SwiftUI入门 - Core Data实体的关联关系(一对一、一对多、多对多)》
如需获取其他SwiftUI知识点,请移步至SwiftUI入门专栏
前言
在上一章笔记《SwiftUI入门 - Core Data包装器@FetchRequest的排序、筛选和分页》中,我们知晓了数据的排序、查询和分页等相关知识点。在本章节中我们将学习Core Data数据分组、聚合和汇总的使用,也是对上一章节内容的补充。
物料
直接沿用上一章的实体和测试数据,如若还不清楚实体的创建与测试数据的导入,请移步至上一章,这里不详细阐述此过程。
创建测试实体
创建测试数据
// 定义测试序列化数据的结构体
struct UserItem{
var name:String;
var score:Int32;
var timestamp: Date;
}
// 初始化测试数据方法
func initTestData(){
let list: [UserItem] = [
UserItem(name: "张三", score: 60, timestamp: Date()),
UserItem(name: "李四", score: 70, timestamp: Date().addingTimeInterval(60)),
UserItem(name: "王五", score: 10, timestamp: Date().addingTimeInterval(3600)),
UserItem(name: "赵六", score: 20, timestamp: Date().addingTimeInterval(120)),
UserItem(name: "刘七", score: 70, timestamp: Date().addingTimeInterval(3600)),
UserItem(name: "周一", score: 40, timestamp: Date().addingTimeInterval(3600)),
UserItem(name: "将二", score: 50, timestamp: Date().addingTimeInterval(5960)),
UserItem(name: "王五", score: 100, timestamp: Date().addingTimeInterval(1230)),
UserItem(name: "张三", score: 70, timestamp: Date().addingTimeInterval(567)),
UserItem(name: "建安", score: 90, timestamp: Date().addingTimeInterval(345)),
];
// 遍历存入缓存
for element in list{
let user = User(context: viewContext)
user.name = element.name
user.score = element.score
user.timestamp = element.timestamp
}
do {
// 保存数据入库
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
分组
分组的作用不言而喻,在诸多的业务场景中,例如中台、大屏和数据分析等场景均在使用,是对某个特定事物的分组,以达到归类的目的
import SwiftUI
import CoreData
struct GroupView: View {
// 获取 BestBeforeApp 传入的环境变量并赋值给 viewContext
@Environment(\.managedObjectContext) var viewContext
// 分组包装器
@SectionedFetchRequest<Int32, User>(
sectionIdentifier: \.score,
sortDescriptors: [
NSSortDescriptor(keyPath: \User.score, ascending: false)
]
)
var users: SectionedFetchResults<Int32, User>
var body: some View{
// 滚动视图
ScrollView{
VStack{
// 循环分组用户列表
ForEach(users){section in
HStack{
// 获取当前分组值
Text("\(section.id)").padding(8)
Spacer()
}
.frame(maxWidth: .infinity)
.foregroundColor(Color.white)
.background(Color.gray)
// 循环当前分组用户列表
ForEach(section) { item in
HStack{
Text(item.name ?? "")
Spacer()
Text(formatDate(date: item.timestamp!))
}
}
}
}.padding()
}
}
// 日期格式化函数
func formatDate(date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter.string(from: date)
}
}
代码执行逻辑:
- 我们使用
@SectionedFetchRequest<Int32, User>
来获取分组数据,其泛型Int32
表示约束sectionIdentifier
的数据类型为Int32
- 形参
sectionIdentifier
接受一个实体属性,且这个属性必须与上述@SectionedFetchRequest<Int32, User>
中左侧约束类型一致,代码中传入了\.score
表示用实体属性score
的值作为分组 - 然后我们通过
ForEach
的嵌套遍历展示分组数据,循环体中的section.id
为实体属性值
输出结果如下:
当然我们也可以使用用户名来分组,也就是用 name
属性的值
// 分组包装器
@SectionedFetchRequest<String, User>(
sectionIdentifier: \.name,
sortDescriptors: [
NSSortDescriptor(keyPath: \User.score, ascending: false)
]
)
var users: SectionedFetchResults<String, User>
注意保持泛型的数据类型与分组属性的数据类型一致,这里我们将数据类型替换成 String
聚合与汇总
直接贴代码
import SwiftUI
import CoreData
struct MaxValueView: View {
// 获取 BestBeforeApp 传入的环境变量并赋值给 viewContext
@Environment(\.managedObjectContext) var viewContext
var body: some View{
Text("score最大值为:\(String(fetchMaxValue()!))")
}
// 获取最大值
func fetchMaxValue() -> Int32? {
// 创建一个FetchRequest,结果类型为字典
let fetchRequest = NSFetchRequest<NSDictionary>(entityName: "User")
fetchRequest.resultType = .dictionaryResultType
// 创建一个NSExpression,用于表示"value"属性
let expression = NSExpression(forKeyPath: "score")
// 创建一个NSExpression,用于计算平均值
let averageExpression = NSExpression(forFunction: "max:", arguments: [expression])
// 创建一个NSExpressionDescription,用于描述计算结果的属性
let expressionDescription = NSExpressionDescription()
expressionDescription.name = "maxValue" // 属性的名称
expressionDescription.expression = averageExpression // 属性的表达式
expressionDescription.expressionResultType = .integer32AttributeType // 属性的数据类型
// 将NSExpressionDescription添加到FetchRequest的propertiesToFetch数组中
fetchRequest.propertiesToFetch = [expressionDescription]
do {
// 执行FetchRequest,获取结果
let result = try viewContext.fetch(fetchRequest)
// 从结果中提取计算的平均值,并返回
if let averageValue = result.first?["maxValue"] as? Int32 {
return averageValue
}
} catch {
// 处理错误
print("Error fetching average value: \(error)")
}
// 如果发生错误或没有找到结果,则返回nil
return nil
}
}
代码执行逻辑:
- 我们通过
NSFetchRequest
创建了查询请求 - 使用
NSExpression
的forKeyPath
指定聚合属性名 - 使用
NSExpression
的forFunction
指定聚合函数,这里也可以替换为min
sum
average
等函数。 - 创建
expressionDescription
结构体,添加到fetchRequest.propertiesToFetch
数组中 - 然后通过
viewContext.fetch
获取到score
的最大值 - 最后在
body
中使用Text
展示
总结
- 我们了解到
Core Data
提供了@SectionedFetchRequest
包装器中的sectionIdentifier
来指定分组属性实现数据分组 - 使用了两个
ForEach
嵌套循环去渲染分组后的结果,其中循环体中的section.id
为实体属性值 - 使用
NSExpression
与NSExpressionDescription
来指定聚合函数,并添加到fetchRequest.propertiesToFetch
数组中获取聚合结果
别错过精彩内容,快来关注我们的微信公众号【寻桃】!