Search code examples
iossortingdatetimecore-datanssortdescriptor

Sort on (before or after today) in NSSortDescriptor


I have a NSFetchedResultsController that feeds a UITableViewController with Appointment entities that have, among others, a property dateTime.

My goal is to have a list of appointments sorted like this:

  1. Today
  2. Tomorrow
  3. Day after tomorrow
  4. Yesterday
  5. Day before yesterday

In other words: First sort on whether it's before or after today, then on dateTime. So first sort on (dateTime > %TODAY%), then on dateTime itself (future ascending, past descending).

Is there any way to do this? My problem is that I can't use %TODAY% in a sort descriptor, but maybe there's another way to achieve the same visual result.

UPDATE: Be aware the accepted answer is not possible with a NSFetchedResultsController. I chose another solution, ordering the Appointments before showing the view., saving that to the database, and let the NSFetchedResultsController pick up the changes.

UPDATE 2: Accepted answer did to do exactly what I wanted. My implementation:

let futureAppointments = appointments.filter({ $0.dateTime != nil && $0.dateTime?.compare(Date()) == .orderedDescending })
    .sorted(by: { $0.dateTime?.compare($1.dateTime! as Date) == .orderedAscending })

let pastAppointments = appointments.filter({ $0.dateTime != nil && $0.dateTime?.compare(Date()) == .orderedAscending })
    .sorted(by: {  $0.dateTime?.compare($1.dateTime! as Date) == .orderedDescending })

let sorted = futureAppointments + pastAppointments

for (index, appointment) in sorted.enumerated() {
    appointment.order = NSNumber(integerLiteral: index)
}

Solution

  • It's only idea. May be you can create sortdescriptor like this:

    NSSortDescriptor(key: "test", ascending: true) { (v1, v2) -> ComparisonResult in
          guard let d1 = v1 as? Date else
          {
              return .orderedAscending
          }
          guard let d2 = v2 as? Date else
          {
            return .orderedAscending
          }
          if abs(d1.timeIntervalSinceNow) < abs(d2.timeIntervalSinceNow) { return .orderedAscending }
          if abs(d1.timeIntervalSinceNow) > abs(d2.timeIntervalSinceNow) { return .orderedDescending }
          return .orderedSame
    }
    

    I think it will be work.