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]
  • Forgot to mention, I am using datePickerStyle = .wheels

Add a Comment

Accepted Reply

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

  • Thank you for the explanation DMG, you are correct it is a String. I will look into changing that.

  • Hi DMG,

    I have been without any luck to get this to sort correctly, so I changed the ascending to false and that works, but of course I can't find how to change the attribute 'date' from a string. I changed it in Attributes to a Date but of course many errors pop up ( Cannot assign value of type 'Date?' to type 'String?') I read somewhere but cannot find it on how to convert a 'date' String to use for sorting? Any thoughts on where to find an article I can read. Thanks for any help.

    Drew

Add a Comment

Replies

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

  • Thank you for the explanation DMG, you are correct it is a String. I will look into changing that.

  • Hi DMG,

    I have been without any luck to get this to sort correctly, so I changed the ascending to false and that works, but of course I can't find how to change the attribute 'date' from a string. I changed it in Attributes to a Date but of course many errors pop up ( Cannot assign value of type 'Date?' to type 'String?') I read somewhere but cannot find it on how to convert a 'date' String to use for sorting? Any thoughts on where to find an article I can read. Thanks for any help.

    Drew

Add a Comment

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

  • Thanks DMG, I do t use GitHub, do you feel it’s needed as far as development of Apps?

    I did change date in attribute’s from a String to a Date, unfortunately 4 errors pop up ( Cannot assign value of type 'Date?' to type 'String?' ) they are all in a “row” from a cell in a tableView. I will keep digging to resolve this issue and if I can figure it out I will post the code. i appreciate all the time you have spent discussing this issue with me.

    Drew

Add a Comment

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

  • Oops, I forgot to mark the realDate var as public. It might work without being public, but I doubt it. Regards, Michaela

  • Thank you Michaela, I will try this out when I get back tonight. I appreciate your time.

    Andrew

  • Hi Michaela, It is sorted by the highest (first)day number. They sorted like below:

    Mar 9, 2022 Mar 8,2022 Mar 7, 2021 Mar 6,2022 Mar 5, 2022 Mar 4, 2022 Mar 3, 2022 Mar 26, 2022 Mar 26,2021 Mar 23, 2022 Mar 11, 2022 etc...

    I did not know where to put the 'public' on the var realDate, so I left it out. maybe that is the issue?

    Thanks again,

    Drew

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