swiftcore-datapredicatensdatecomponents

Predicate for fetching the data for a particular year from Core Data in SwiftUI


I am developing a SwiftUI Budget management application which tracks the Expense and Income of the user. The application uses CoreData for managing the data management. The managed object has date as one property.

I use the below fetch request to get the data

 @SectionedFetchRequest(sectionIdentifier: \Expence.itemMonthYearShort, sortDescriptors: [SortDescriptor(\Expence.date, order: .forward)])
    private var expences : SectionedFetchResults<String, Expence>

This will give grouped data like Expense for Sept 2021

The managed object model is as below

extension Expence {
    
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Expence> {
        return NSFetchRequest<Expence>(entityName: "Expence")
    }
    
    @NSManaged public var amount: Double
    @NSManaged public var date: Date
    @NSManaged public var id: UUID?
    @NSManaged public var type: Int16
    @NSManaged public var title: String?
    @NSManaged public var note: String?
    @NSManaged public var category: Category?
    
    public var wrappedNotes : String{
        note ?? ""
    }
    public var wrappedTitle : String{
        title ?? ""
    }
    
    @objc var itemMonthYear : String{
        return date.formatted(.dateTime.month(.wide).year())
    }
    @objc var itemMonthYearShort : String{
        return date.formatted(.dateTime.month(.abbreviated).year())
    }
    
    @objc var itemMonthYearDescending : String{
        return date.formatted(.dateTime.month(.wide).year()) + " "
    }
    
    @objc var itemMonth : String{
        return date.formatted(.dateTime.month(.abbreviated
                                             ))
    }
    @objc var itemYear : String{
        return date.formatted(.dateTime.year())
    }
}
extension Expence : Identifiable {

}

I want to filter the data based on year. For example I want to show the expenses for particular year. How can I write the predicate for that ?


Solution

  • To find the start and end points of a particular year, Calendar has a dateInterval method. For example:

    let today = Date.now // => November 18, 2022
    let thisYear = Calendar.current.dateInterval(of: .year, for: today)! // NB: note this is an Optional
    
    let predicate = NSPredicate(format: "date >= %@ AND date < %@", thisYear.start as NSDate, thisYear.end as NSDate)
    

    If you want to look for a specific year, instead of using today's date as a starting point you can create a date whose year matches the one you want to filter by, and so on.