1

I'm using NSSortDescriptors to sort an Array by the date element. The date is set as String using a formatter which formats in the style : "dd/MM/yy HH:mm". This date element is stored inside dictionaries which are all stored in the array. Parts of my code for this are below:

// Date Formatting
let currentTime = Date()
let timeFormatter = DateFormatter()
timeFormatter.locale = Locale.current
timeFormatter.dateFormat = "HH:mm dd/MM/yy"
let convertedTime:String! = timeFormatter.string(from: currentTime)

// Descriptor
let descriptorD: NSSortDescriptor = NSSortDescriptor(key: "Date", ascending: false)

// Dictionary
let newUserRecord = [
    "Name"  : enteredName!,
    "Score" : self.gameScore,
    "Date"   : convertedTime
] as [String : Any]

// Sorting 
newUserArray.sort(using: [descriptorD])

However my problem is the date is only being sorted by the time (HH:mm) and not taking in account the (dd/MM/yy) part. For example if I sort by the date and have a date of 13:12 19/11/16 and a date of 09:12 18/11/16 the 09:12 date will appear first even though it should be the 13:12 as it is a day later. How do I fix this?

fpg1503
  • 7,492
  • 6
  • 29
  • 49
  • Just use a `Calendar` and compare the dates inside your `sort` closure – fpg1503 Nov 08 '16 at 10:54
  • Why are you changing it to a string? If you want to store Date information then store it as a Date object. You also get the added benefit that Date knows how to sort itself and it will fix your proble, – Fogmeister Nov 08 '16 at 10:54
  • 1
    @fpg1503 the dates inside the `sort` closure are Strings. Not Dates. – Fogmeister Nov 08 '16 at 10:55
  • 1
    You won't be able to use the `sortDescriptor` with the `DateFormatter`. Something like that should do the trick: `yourArray.sort({ timeFormatter(dateFromString:$0.Date) > timeFormatter(dateFromString:$1.Date) })` or something looks alike. – Larme Nov 08 '16 at 10:55
  • Ok will try @Larme –  Nov 08 '16 at 10:57
  • @Fogmeister he'll have to convert it to `Date`s anyway – fpg1503 Nov 08 '16 at 10:57
  • @fpg1503 but it starts out as Dates. And then is converted to Strings. Just don't convert it to Strings and the problem goes away. – Fogmeister Nov 08 '16 at 10:58
  • @Fogmeister However the problem is when I store it as a date the formatting of it weird. I need it only to show the date and time format –  Nov 08 '16 at 10:58
  • @fpg1503 Is there any way to format the date though? –  Nov 08 '16 at 10:59
  • Here is the duplicate question: http://stackoverflow.com/questions/24130026/swift-how-to-sort-array-of-custom-objects-by-property-value You just have to use your `(NS)DateFormatter` to transform your object date which is a `(NS)String` into a `(NS)Date`. – Larme Nov 08 '16 at 10:59
  • @John_Sm1 but you only need to format it when you display it on the screen. The computer doesn't know or care what it looks like. Format the date when you want to put it into a human readable format so that the user can see what the date means. Otherwise keep it as a Date object. – Fogmeister Nov 08 '16 at 10:59
  • @Fogmeister I assumed the dates were being persisted as Strings – fpg1503 Nov 08 '16 at 11:00
  • @Fogmeister ok so If I stored it as a date would it work if I converted them to a string and formmated in the cellForRow method –  Nov 08 '16 at 11:03
  • @John_Sm1 yes, that would be the ideal way to work with Dates and display them to a label etc... Store them as Dates as they're much easier to work with that way. Then format them when you want to display them. (Like in cellForRow). One thing to note, though. DateFormatter is very expensive to create. Make one of them (as a `let` on the class) and then use it to format. Don't create it each time in `cellForRow`. – Fogmeister Nov 08 '16 at 11:05
  • @Fogmeister Ok will try to –  Nov 08 '16 at 11:08

1 Answers1

2

This is the object oriented Swift way:

Declare a struct rather than a dictionary and include the time formatter

struct User {

   let timeFormatter : DateFormatter = {
      let formatter = DateFormatter()
      formatter.locale = Locale.current
      formatter.dateFormat = "HH:mm dd/MM/yy"
      return formatter
   }()

   let name : String
   let score : Int
   let time : Date

   var convertedTime : String {
      return timeFormatter.string(from: time)
   }

}

Declare an array of the User type and add two instances

var newUserArray = [User]()

newUserArray.append(User(name: "Foo", score: 12, time: Date().addingTimeInterval(1000.0)))
newUserArray.append(User(name: "Bar", score: 78, time: Date()))

Sort the array by time descending

newUserArray.sort(by: {$0.time > $1.time })

And print the formatted date

print(newUserArray[0].convertedTime)
vadian
  • 274,689
  • 30
  • 353
  • 361
  • I was going to add something like this. I personally think that the date formatter should not belong in the data object though. It's a view thing that should probably be in the view controller or somewhere similar. Using a struct is a much better method than using a dictionary though. – Fogmeister Nov 08 '16 at 11:38