You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift数据模型问题:无法筛选并计算盆景待办任务剩余天数

问题描述

我有两个Core Data模型:BonsaiModel和TaskModel,代码如下:

@Model
final class BonsaiModel {
    var uuid: UUID
    var timestamp: Date
    var purchased: Date
    var price: Int
    var name: String
    var isFavorite: Bool
    var age: Int

    @Attribute(.externalStorage)
    var bonsaiImageData: Data?

    @Relationship(deleteRule: .nullify, inverse: \CategoryModel.bonsai)
    var category: CategoryModel?
    @Relationship(deleteRule: .nullify, inverse: \SpeciesModel.bonsai)
    var species: SpeciesModel?

    @Relationship(deleteRule: .cascade, inverse: \TaskModel.bonsai)
    var newTaskModel: [TaskModel] = []

    @Relationship(deleteRule: .cascade, inverse: \BonsaiImageModel.bonsai)
    var bonsaiImages: [BonsaiImageModel] = []

    init(name: String = "",
         isFavorite: Bool = false,
         bonsaiImageData: Data? = nil
    ) {
        self.uuid = UUID()
        self.timestamp = Date()
        self.purchased = Date()
        self.price = Int()
        self.name = name
        self.isFavorite = isFavorite
        self.age = 2023
        self.bonsaiImageData = bonsaiImageData

    }

}

@Model
final class TaskModel {
    var uuid: UUID
    var name: String
    var timestamp: Date
    var isComplete: Bool
    @Relationship(deleteRule: .nullify, inverse: \CareTypeModel.newTask)
    var careType: CareTypeModel?
    var isDue: Date

    var bonsai: BonsaiModel?

    init(
        name: String = "",
        isComplete: Bool = false
    ){
        self.uuid = UUID()
        self.name = name
        self.timestamp = Date()
        self.isComplete = isComplete
        self.isDue = Date()
    }
}

一个盆景可关联多个任务,当前功能运行正常。现在需要实现显示指定盆景的下一个待办任务的剩余天数。视图中获取数据的方式如下:

@Environment(\.locale) private var locale
@State var bonsai = BonsaiModel() // 获取单个盆景实例
@Query private var taskModel: [TaskModel] // 获取所有任务

我的思路是先获取所有任务,筛选出属于指定盆景的任务,再获取isDue日期并计算该日期与当前日期的天数差,但使用filter方法时无法实现预期效果,求可行解决方案。

解决方案

方案1:直接利用Core Data关系获取关联任务

既然BonsaiModel已经通过newTaskModel关联了对应的TaskModel列表,完全不需要查询所有任务再筛选,直接从当前bonsai实例获取关联任务即可,效率更高:

// 从当前盆景实例筛选未完成的任务
let pendingTasks = bonsai.newTaskModel.filter { !$0.isComplete }

方案2:正确筛选全局任务列表(如果必须使用)

如果一定要用@Query获取的全局任务列表,筛选时需匹配盆景的uuid(Core Data对象直接比较可能存在问题,用唯一标识更可靠):

let targetBonsaiUUID = bonsai.uuid
let pendingTasks = taskModel.filter { 
    $0.bonsai?.uuid == targetBonsaiUUID && !$0.isComplete 
}

计算下一个待办任务的剩余天数

筛选出未完成任务后,找出isDue最早的任务,再计算与当前日期的天数差:

// 获取下一个待办任务(isDue最早的未完成任务)
if let nextTask = pendingTasks.min(by: { $0.isDue < $1.isDue }) {
    let calendar = Calendar.current
    let components = calendar.dateComponents([.day], from: Date(), to: nextTask.isDue)
    if let remainingDays = components.day {
        // 根据剩余天数生成显示文本
        let displayText = remainingDays > 0 ? "\(remainingDays)天后到期" : remainingDays == 0 ? "今天到期" : "\(-remainingDays)天已逾期"
        // 这里可以将displayText用于视图展示
    }
} else {
    // 无待办任务的处理逻辑
}

优化:用@Query直接过滤指定盆景的任务

可以在@Query中直接添加过滤条件,避免获取所有任务,提升性能:

// 替换原@Query,动态过滤当前盆景的未完成任务并按到期时间排序
@Query(filter: #Predicate<TaskModel> { task in
    task.bonsai?.uuid == bonsai.uuid && !task.isComplete
}, sort: \TaskModel.isDue) private var pendingTasks: [TaskModel]

此时pendingTasks直接是当前盆景的未完成任务,且按到期时间升序排列,第一个元素就是下一个待办任务,直接取pendingTasks.first即可。

内容的提问来源于stack exchange,提问作者DennisHH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 03:53:18