pAndrei
pAndrei

Reputation: 31

Sorting by month and day with NSSortDescriptor

I am building an app in Swift and I want to display a list with the closest birthdays appearing first on the screen.

e.g. if I have 3 dates (21st of Dec, 21st of May, 21st of Nov) and today is 31st of May. The order shall be 21st of Nov, 21st of Dec and 21st of May.

I am using CoreData and I have an entity called Entry having a date:

extension Entry {

@nonobjc public class func fetchRequest() -> NSFetchRequest<Entry> {
    return NSFetchRequest<Entry>(entityName: "Entry")
}

@NSManaged public var date: Date?
}

And the apparition is made with NSSortDescriptor:

    override func viewWillAppear(_ animated: Bool) {
    if let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext {
        let request: NSFetchRequest<Entry> = Entry.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
        
        if let entriesFromCoreData = try? context.fetch(request) {
            entries = entriesFromCoreData
            tableView.reloadData()
        }
    }
}

I have tried the following, but (1) is giving me a fatal error while extracting a String from KeyPath Swift.KeyPath and (2) is not making the proper sort, taking into consideration the year.

(1)

extension Entry {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Entry> {
    return NSFetchRequest<Entry>(entityName: "Entry")
}
@NSManaged public var date: Date?
var nextBirthday: Date { date?.nextBirthday ?? .distantFuture }
}


extension Date {
var isInToday: Bool { Calendar.current.isDateInToday(self) }
var nextBirthday: Date {
    isInToday ? self : Calendar.current.nextDate(after: Date(), matching: Calendar.current.dateComponents([.month, .day, .hour, .minute], from: self), matchingPolicy: .nextTime)!
}}


override func viewWillAppear(_ animated: Bool) {
    if let context = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer.viewContext {
        let request: NSFetchRequest<Entry> = Entry.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(keyPath: \Entry.nextBirthday, ascending: true)]
        
        if let entriesFromCoreData = try? context.fetch(request) {
            entries = entriesFromCoreData
            tableView.reloadData()
        }
    }
}

(2)

extension NSDate {
dynamic var nextOccurrence : NSDate {
    let calendar = Calendar.current
    let components = calendar.dateComponents([.month, .day], from: self as Date)
    return calendar.nextDate(after: Date(), matching: components, matchingPolicy: .nextTime)! as NSDate
}}


NSSortDescriptor(key: "date.nextOccurrence", ascending: true)

Upvotes: 2

Views: 59

Answers (0)

Related Questions