Swift 2 OptionSetType
Swift 2 OptionSetType
Swift 2/iOS 9 broke my calendar/date code.
What’s going on?
Introduction
If you have an app that does anything with dates, you most likely have code someplace like this.
You get the date’s components, do something with them, and construct a new date from those components.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
if let gregorian = NSCalendar(calendarIdentifier:NSCalendarIdentifierGregorian) { let comps = gregorian.components( .CalendarUnitHour | .CalendarUnitMinute | .CalendarUnitSecond | .CalendarUnitMonth | .CalendarUnitDay | .CalendarUnitYear, fromDate: date) comps.day = something comps.hour = something comps.minute = something comps.second = something etc. return gregorian.dateFromComponents(comps) } |
Broken in iOS 9.
So, the first thought I had was “Did they change the damn names again?” (MonthCalendarUnit for example was previously renamed CalendarUnitMonth).
No, that’s not it. Sort of.
What they did away with was that bitwise or-ing of components.
You do this now to specify the components.
1 2 |
let comps = gregorian.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: NSDate()) |
So, MonthCalendarUnit -> CalendarUnitMonth -> Month?
There’s a “bit” more to it.
RawOptionSetType
The “old” NSCalendarUnit is a RawOptionSetType.
Here is an example definition of options for a hoagie (not a sub – in spite of what The Google says – which are inferior. Hey, the name even implies “sub”standard!).
There’s some boilerplate setup funcs because of the inheritance thing, and then the individual options are defined by bit shifting 1 to create a bitmask.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
struct HoagieIngredients : RawOptionSetType { private var value: UInt = 0 init(_ value: UInt) { self.value = value } // MARK: _RawOptionSetType init(rawValue value: UInt) { self.value = value } // MARK: NilLiteralConvertible init(nilLiteral: ()) { self.value = 0 } // MARK: RawRepresentable var rawValue: UInt { return self.value } // MARK: BitwiseOperationsType static var allZeros: HoagieIngredients { return self(0) } static var None: HoagieIngredients { return self(0) } static var Lettuce: HoagieIngredients { return self(1 << 0) } static var Tomatoes: HoagieIngredients { return self(1 << 1) } static var Onions: HoagieIngredients { return self(1 << 2) } static var Oil: HoagieIngredients { return self(1 << 3) } static var Vinegar: HoagieIngredients { return self(1 << 4) } static var Peppers: HoagieIngredients { return self(1 << 5) } static var Pickles: HoagieIngredients { return self(1 << 6) } static var Salt: HoagieIngredients { return self(1 << 7) } static var Pepper: HoagieIngredients { return self(1 << 8) } static var Oregano: HoagieIngredients { return self(1 << 9) } } |
This is how you would use these options. To set an option, you bitwise-or them into a variable.
1 2 3 4 5 6 7 8 |
struct Hoagie { var name = "Hoagie" var ingredients:HoagieIngredients init() { ingredients = HoagieIngredients.Lettuce | HoagieIngredients.Tomatoes // or simply ingredients = .Lettuce | .Tomatoes |
You can see if an option is set by doing a bitwise-and like this.
1 2 3 |
func hasTomatoes() -> Bool { return self.ingredients & .Tomatoes != .None } |
Just like everywhere else you deal with bits. Or to set, And to get, Shift to create masks.
OptionSetType
The new! and improved! way in Swift 2 is to use OptionSetType instead of RawOptionSetType.
No bits, just values. You can even define “aggregate” options like “TheWorks” in this example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
struct HoagieIngredients : OptionSetType { let rawValue: UInt static let None = HoagieIngredients(rawValue: 0) static let Lettuce = HoagieIngredients(rawValue: 1) static let Tomatoes = HoagieIngredients(rawValue: 1 << 1) static let Onions = HoagieIngredients(rawValue: 1 << 2) static let Oil = HoagieIngredients(rawValue: 1 << 3) static let Vinegar = HoagieIngredients(rawValue: 1 << 4) static let Peppers = HoagieIngredients(rawValue: 1 << 5) static let Pickles = HoagieIngredients(rawValue: 1 << 6) static let Salt = HoagieIngredients(rawValue: 1 << 7) static let Pepper = HoagieIngredients(rawValue: 1 << 8) static let Oregano = HoagieIngredients(rawValue: 1 << 9) static let TheWorks:HoagieIngredients = [.Lettuce, .Tomatoes, .Onions, .Oil, .Vinegar, .Pickles, .Peppers, .Salt, .Pepper, .Oregano] } |
And then to use them in our domain struct:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
struct Hoagie { var name = "Hoagie" var ingredients:HoagieIngredients init() { ingredients = [.Lettuce, .Tomatoes] // or even ingredients = .TheWorks } mutating func addOnions() { ingredients.insert(.Onions) } func hasLettuce() -> Bool { return ingredients.contains(.Lettuce) } mutating func regular() { ingredients.unionInPlace(.TheWorks) } mutating func plain() { ingredients.subtractInPlace(.TheWorks) } func hasIngredients() -> Bool { return !ingredients.isEmpty } etc. |
As you see, OptionSetType provides several funcs for operating on the options.
Is this easier than dealing with bits?
Well, I still use The Emacs, so I’m the wrong guy to ask.
Some Other examples
Here are a few more places you might find these new option sets.
UIViewAutoresizing is an OptionSetType.
1 2 3 4 5 6 |
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) addSubview(view) // pre Swift 2.0 view.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight // Swift 2.0 view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] |
UIUserNotificationType is an OptionSetType.
1 2 3 4 5 6 7 8 |
// pre Swift 2.0 let settings = UIUserNotificationSettings(forTypes: UIUserNotificationType.Alert | UIUserNotificationType.Badge, categories: nil) // Swift 2.0 let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge], categories: nil) UIApplication.sharedApplication().registerUserNotificationSettings(settings) if settings.types.contains(.Badge) { // whatever } |
You will find many more examples and you try to run your old pre 2.0 code.
Summary
OptionSetType has replaced RawOptionSetType.
You don’t have to deal with bit level operations now.
At least, not for options.
Resources
Do you want a really good hoagie?
Amato Brothers Deli
They aren’t paying me for this plug. I just eat there all the time.
Thanks a lot, mate. This was fantastic. I just could get it to get suggested like the .Normal or .Highlighted. Is there a way to do this? Good job anyway. Cheers.