Playing with extensions in Swift

By now we all know Apple introduced a new language called Swift. Personally, despite not really having tinkered with it much yet, Swift seems to add a lot of great stuff like generics, higher order functions and extensions, to name a few.

In Objective-C you can use categories to add behavior to existing classes. Swift offers what Apple calls extensions, which are similar to categories in the way that you can add behavior to a class, but without the need to explicitly import the category header. Instead, every type you write an extension for automatically gains the functionality of that extension. Extensions aren't limited to just classes either.

As a quick example, lets see if we can make working with relative dates a bit easier. In Ruby (when using Rails), you can write stuff like this:

1.hour.ago # => 2014-06-11 16:38:54 +0200  
25.minutes.from_now # 2014-06-11 18:04:02 +0200  
10.minutes.from_now.future? # => true  

Rails does this by adding behavior to the Fixnum class; Ruby's class for handling integer values. Wouldn't it be nice to be able to do the same in Swift? Since we can now also extend NSTimeInterval, it's dead-easy.

extension NSTimeInterval {  
    // Support for singular names
    var second: NSTimeInterval { return seconds }
    var minute: NSTimeInterval { return minutes }
    var hour: NSTimeInterval { return hours }
    var day: NSTimeInterval { return days }
    var week: NSTimeInterval { return weeks }
    var month: NSTimeInterval { return months }
    var year: NSTimeInterval { return years }

    var seconds: NSTimeInterval {
        let components = NSDateComponents()
        components.second = Int(self)
        return relativeTimeIntervalFromComponents(components)
    }

    var minutes: NSTimeInterval {
        let components = NSDateComponents()
        components.minute = Int(self)
        return relativeTimeIntervalFromComponents(components)
    }

    var hours: NSTimeInterval {
        let components = NSDateComponents()
        components.hour = Int(self)
        return relativeTimeIntervalFromComponents(components)
    }

    var days: NSTimeInterval {
        let components = NSDateComponents()
        components.day = Int(self)
        return relativeTimeIntervalFromComponents(components)
    }

    var weeks: NSTimeInterval {
        let components = NSDateComponents()
        components.day = Int(self) * 7
        return relativeTimeIntervalFromComponents(components)
    }

    var months: NSTimeInterval {
        let components = NSDateComponents()
        components.month = Int(self)
        return relativeTimeIntervalFromComponents(components)
    }

    var years: NSTimeInterval {
        let components = NSDateComponents()
        components.year = Int(self)
        return relativeTimeIntervalFromComponents(components)
    }

    func relativeTimeIntervalFromComponents(components: NSDateComponents) -> NSTimeInterval {
        let calendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
        let date = calendar.dateByAddingComponents(components, toDate: NSDate(), options: nil)
        return NSDate().timeIntervalSinceDate(date)
    }

    var fromNow: NSDate { return NSDate(timeIntervalSinceNow: -self) }
    var ago: NSDate { return NSDate(timeIntervalSinceNow: self); }
}

Via this extension, we added a few computed properties to NSTimeInterval, which all return newly computed values when they're called. In this case, we just want to convert minutes, hours, days etc. into seconds. We can then add or deduct those values from the current time, giving us dates in the future or the past.

The values returned by fromNow and ago are of type NSDate. Since we also want to check for a date is in the future, we wrote a small extension on NSDate as well which lets us do just that. We can now proceed to write stuff like:

1.hour.ago // "11 jun. 2014 16:38"  
25.minutes.fromNow // "11 jun. 2014 18:04"  
10.minutes.fromNow.isFuture // true  

Of course, this example is trivial, but it does show how easy it is to create a useful extension. I can't wait to see what clever uses people will come up with.

Erik van der Wal

Erik van der Wal

I love building things with Swift, Objective-C and Ruby and tinkering with technologies like Golang and Elixir.

comments powered by Disqus