Using NSSortDescriptor(key: "date", ascending: true

I have a TableViewController that I am using (datePicker) to add a date to the note with other Attributes. I am using CoreData and fetch to populate another TableViewController with the results. When it populates it shows up with the month: Dec 20, 2021, Feb 1, 2022, Feb 10, 2022, Feb 17, 2022, Feb 2, 2022, Jan 31, 2022, Jan 4, 2022 and so on ... How can I get it to sort correctly? Newest on top Feb 17, 2022 Feb 10, 2022 etc...

Thanks in advance.

let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
Answered by DelawareMathGuy in 705434022

hi,

you do not show the definition of your Core Data entity, nor the type of its attribute "date."

since the list of dates appears to be sorted alphabetically, i am guessing that you defined the date attribute in Core Data as a String (not as a Date) and that you are storing strings in what you call "date." that would explain the result above.

(BTW: to have Core Data sort by the Date type, you would want "ascending: false" for the NSSortDescriptor so that the most recent date comes out first.)

hope that helps,

DMG

Forgot to mention, I am using datePickerStyle = .wheels

Accepted Answer

hi,

you do not show the definition of your Core Data entity, nor the type of its attribute "date."

since the list of dates appears to be sorted alphabetically, i am guessing that you defined the date attribute in Core Data as a String (not as a Date) and that you are storing strings in what you call "date." that would explain the result above.

(BTW: to have Core Data sort by the Date type, you would want "ascending: false" for the NSSortDescriptor so that the most recent date comes out first.)

hope that helps,

DMG

Drew,

some quick comments, since i cannot see your code (if it were available on GitHub, that might be of interest).

  • in the Core Data model editor, find the attribute date in whatever entity you are working with and check that its Type shows as Date. if it shows as String there, just click on the String value and choose Date as the Type from the pull-down menu. (you may already have done this, but i cannot tell from your question.) so Core Data knows the values are dates, and sorting with ascending: false will give most recent dates first, oldest dates last.
  • Xcode will give your app/program access to the date attribute as a Swift variable var date: Date? defined on the class object for your entity. notice that this is an optional Date in Swift, so treat it accordingly when reading it.
  • all your existing code that reads from the date attribute or assigns to the date attribute should be working with real Date objects in Swift. you cannot assign a String to a Date? object; but really, other than for display purposes, you should not be working with string values of dates in your code anyway, since constantly translating back and forth between Strings and Dates is hard.

hope that helps, DMG

There is a way of fixing the problem without restructuring your CoreData model and then recreating the CoreData records. However, this can be a bit tricky if you haven't had experience with manually generating class definitions for CoreData entities. In essence the solution is this:

In Xcode's CoreData Model editor, click on the Entity you need to fix, make sure that the Inspector panel is open (far right of Xcode screen) then change the Codegen option to Manual/None (it's probably set at "Class definition").

Then (still in the Model editor) on the Xcode menu bar (far top of screen) select Editor, CreateNSManagedObject Subclass and follow the prompts to create Swift files in your project for the CoreData Entity classes.

Create a new Swift file for an extension to your Entity. For example, if your Entity is called "Commitment", create a file called "Commitment_Developer" and then use code like this:

import Foundation
extension Commitment  {
    var realDate : Date? {
        guard let strDate = strAttribute else {  // where "strAttribute" is the name of your CoreData date-as-string attribute
            return nil
        }
        let df = DateFormatter()
        df.timeZone = NSTimeZone.local
        df.dateFormat = "MMM d, yyyy"
        guard let rDate = df.date(from:strDate) else {
            return nil
        }
        return rDate
    }
}

Then, in your NSSortDescriptor use the "realDate" property that was created in the extension. Apart from that, your code remains the same as now - without any refactoring of your CoreData model or actual data. You also have the ability to use the "realDate" elsewhere whenever you need access to a system date, as opposed to a string.

Note that "realDate" is Optional, so needs to be unwrapped upon use. It could be defined as non-optional, if you're really confident in the integrity of the string-dates, but then there's a problem if you later try to use iCloud syncing with non-optionals.

I hope this helps. best wishes and regards, Michaela

Further to my comment on my previous answer, a computed property (realDate) cannot be used as an NSSortDescriptor key (my mistake, sorry) but FetchedResults can be sorted, upon return, using Swift array's .sorted(by: {$0.realDate! > $1.realDate!}) and then used in your table (or whatever), but you have to be careful in converting the String date to a system date, or in dealing with nil values coming from the conversion.

In my sample code for realDate, the dateFormat should be df.dateFormat = "MMM dd, yyyy" not df.dateFormat = "MMM d, yyyy". Maybe check your date strings for variations to the string format, just to be sure? I made a couple of typos when creating test data and that messed the solution up!!

If you do have variations, you can test for them in the reaDate extension code and convert accordingly.

Regards, Michaela

Using NSSortDescriptor(key: "date", ascending: true
 
 
Q