SwiftUI入门 - Core Data包装器@FetchRequest的排序、筛选和分页

置顶

菜鸟入门笔记,如有谬误之处还请大佬指出

深耕细作 笃行致远

前言

在上一篇文章SwiftUI入门 - Core Data初探与实践中,我们已知晓 Core Data 模型、实体和实体属性的创建,并通过编写 CRUD 函数完成了实体数据的增删改查操作。在实现查询数据的过程中对 @FetchRequest 属性包装器的使用则是一笔带过,并未对其进行详细的了解。本文中将对Core Data的排序、筛选和分页的使用方法进行学习和探究。

物料

创建测试实体

在下方的截图中,我们创建了 User 实体,并为其添加三个属性:

image.png

创建测试数据

// 定义测试序列化数据的结构体
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)")
    }
}

在上方的代码中,我们定义了一个结构体将测试数据存储到数组中,以便于循环入库

粘贴上方代码到你的应用中,执行 initTestData() 函数即可,如若 viewContext 与你的名称不同,请稍作调整

排序

在常见应用使用场景中,我们通常需要对数字或字符串字段按字段的先后顺序和字段值的升序/降序进行排序,Core Data包装器@FetchRequest同样提供了这种能力

@FetchRequest(
        // 排序描述
        sortDescriptors: [
                // 排序规则
                NSSortDescriptor(keyPath: User.timestamp, ascending: true),
                NSSortDescriptor(keyPath: User.score, ascending: false)
        ]
)
var users: FetchedResults

var body: some View{
    VStack{
        ForEach(users){item in
            HStack{
                Text(item.name ?? "")
                Spacer()
                Text("(item.score)")
                Spacer()
                Text(formatDate(date: item.timestamp!))
            }.padding()
        }

        Spacer()

        Button {
            initTestData()
        } label: {
            Text("初始化测试数据")
        }
    }
}

// 日期格式化函数
func formatDate(date: Date) -> String {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    return formatter.string(from: date)
}
  1. 1. 我们使用sortDescriptors对排序进行描述,sortDescriptors 接受一个数组,允许对多个字段(属性)进行排序
  2. 2. 使用NSSortDescriptor结构体添加排序规则,keyPath为实体的属性名,ascending则决定升序/降序

排序后展示结果如下:

image.png

筛选

在SwiftUI中,@FetchRequestpredicate 参数用于过滤 Core Data 中的数据。通过使用 predicate 可以指定特定的条件,只获取符合条件的数据,从而对数据进行筛选和显示。

    @FetchRequest(
        predicate:NSPredicate(format: "name like %@", argumentArray: ["张三"])
    )
    var users: FetchedResults

format: 等于是SQL中 where 语句,支持包括[or, >, <, =, like]等 SQL中所有的操作符,%@作为占位符,最终会被argumentArray数组中的参数依次替换

查询结果如下:

image.png

分页

在研究分页时发现@FetchRequest并没有直接提供分页的能力,它会一次性获取所有满足条件的数据放入内存,那么在数据过大的情况下必然会出现页面卡顿或内存溢出的问题。鉴于这种情况,我们采用手动组装请求来获取数据。

由于此部分代码较多,代码执行逻辑写到代码注释中

    import SwiftUI
    import CoreData

    struct CoreDataView: View {
        // 获取 BestBeforeApp 传入的环境变量并赋值给 viewContext
        @Environment(.managedObjectContext) var viewContext
        // 设置每页数据项数
        let pageSize = 3
        // 当前页数
        @State private var currentPage = 1
        // 总页数
        @State private var totalPage = 1

        var body: some View{
            // 懒加载布局
            LazyVStack{
                // 循环数据从 fetchUsers() 方法中获取
                ForEach(fetchUsers(), id: .self){item in
                    HStack{
                        Text(item.name ?? "")
                        Spacer()
                        Text("(item.score)")
                        Spacer()
                        // formatDate() 方法格式化日期时间
                        Text(formatDate(date: item.timestamp!))
                    }.frame(height: 50)
                }
            }.onAppear {
                // 获取总页数
                fetchTotalPage()
                // 初始化加载第一页数据
                fetchNextPage()
            }.padding()
            
            Spacer()
            
            HStack{
                Button {
                    fetchPrePage()
                } label: {
                    Text("上一页")
                }
                Spacer()
                Button {
                    fetchNextPage()
                } label: {
                    Text("下一页")
                }
            }.padding()
        }
        
        // 获取数据列表
        private func fetchUsers() -> [User] {
            // 通过 User 类中内置的 fetchRequest() 生成一个请求对象
            let fetchRequest: NSFetchRequest = User.fetchRequest()
            // 添加排序描述
            fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: User.timestamp, ascending: true)]
            // 设置分页条数
            fetchRequest.fetchLimit = pageSize
            // 设置偏移量(即从第几条数据开始获取)
            fetchRequest.fetchOffset = (currentPage - 1) * pageSize

            do {
                // 通过 viewContext.fetch() 直接获取数据
                let fetchedItems = try viewContext.fetch(fetchRequest)
                return fetchedItems
            } catch {
                print("Error fetching items: (error)")
                return []
            }
        }
        
        // 获取总页数
        private func fetchTotalPage() {
            // 生成 User 请求对象
           let fetchRequest: NSFetchRequest = User.fetchRequest()
           do {
                // 获取总条数
               let totalCount = try viewContext.count(for: fetchRequest)
               totalPage = Int(ceil(Double(totalCount) / Double(pageSize)))
           } catch {
               print("Error fetching total page count: (error)")
               totalPage = 1
           }
       }
       
       // 上一页函数
       func fetchPrePage(){
            if(currentPage>1){
                currentPage -= 1
            }
        }
        
        // 下一页函数
        func fetchNextPage() {
            // 计算下一页页数
            if(currentPage < totalPage){
                currentPage += 1
            }
        }
        
        // 日期格式化函数
        func formatDate(date: Date) -> String {
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            return formatter.string(from: date)
        }
    }

image.png

总结

  1. 我们通过 @FetchRequest 的形参 sortDescriptorsNSSortDescriptor结构体来对查询数据进行排序
  2. 使用 predicate 进行数据筛选过滤,了解到 NSPredicate 中的 format 其实就是 SQL 中的 where 语句
  3. 在研究分页时发现 @FetchRequest 并不直接支持分页,需要通过 NSFetchRequest 的实例来组装请求
  4. 还学习到了 Swift 中 DateFormatter 日期格式化类的使用

别错过精彩内容,快来关注我们的微信公众号【寻桃】!

展开阅读全文

页面更新:2024-05-04

标签:升序   字段   初始化   实体   函数   属性   入门   类型   结构   测试   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top