Royalty Club Royalty Club - 2 months ago 31
Swift Question

Swift / Spring 'Date components' error

Here is the line of code that receives the issue code:

let components: NSDateComponents = calendar.components(unitFlags, fromDate: earliest, toDate: latest, options: [])


Issue code:

'components' produces 'Date Components', not the expected contextual result type 'NSDateComponents'


Additional code:

public func timeAgoSinceDate(date:NSDate, numericDates:Bool) -> String {
let calendar = NSCalendar.current
let unitFlags: NSCalendar.Unit = [NSCalendar.Unit.minute, NSCalendar.Unit.hour, NSCalendar.Unit.day, NSCalendar.Unit.weekOfYear, NSCalendar.Unit.month, NSCalendar.Unit.year, NSCalendar.Unit.second]
let now = NSDate()
let earliest = now.earlierDate(date as Date)
let latest = (earliest == now as Date) ? date : now
let components: NSDateComponents = calendar.components(unitFlags, fromDate: earliest, toDate: latest, options: [])

Answer

Swift 3 introduces a bunch of new value types for existing Foundation class types, such as Date for NSDate, Calendar for NSCalendar. Some of the reasons were

  • to provide proper value semantics,
  • let and var instead of mutable and immutable variants,
  • more "Swifty" APIs.

The details can be found in

You can bridge between the value type and the corresponding NS type (as you did with date as Date), and when existing Foundation APIs are imported into Swift, the types are bridged automatically.

Therefore NSCalendar.current returns a Calendar instead of NSCalendar, and calendar.components returns DateComponents instead of NSDateComponents.

Sometimes the Xcode migrator can fix these issues for you. If not, you have to look up the new types and their methods.

In your case, a Swift 3 port would look like this:

func timeAgoSinceDate(date: Date, numericDates: Bool) -> String {
    let calendar = Calendar.current
    let components: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfYear, .month, .year, .second]
    let now = Date()
    let (earliest, latest) = (now <= date) ? (now, date) : (date, now)
    let dateComponents = calendar.dateComponents(components, from: earliest, to: latest)

    // ...
}

Note that Date is Comparable, therefore you can compare dates directly as in now <= date. This is not possible with NSDate (unless you implement it explicitly).

Comments