How to create NS_OPTIONS-style bitmask enumerations in Swift?

Swift 3.0

Almost identical to Swift 2.0. OptionSetType was renamed to OptionSet and enums are written lower case by convention.

struct MyOptions : OptionSet {
    let rawValue: Int

    static let firstOption  = MyOptions(rawValue: 1 << 0)
    static let secondOption = MyOptions(rawValue: 1 << 1)
    static let thirdOption  = MyOptions(rawValue: 1 << 2)
}

Instead of providing a none option, the Swift 3 recommendation is to simply use an empty array literal:

let noOptions: MyOptions = []

Other usage:

let singleOption = MyOptions.firstOption
let multipleOptions: MyOptions = [.firstOption, .secondOption]
if multipleOptions.contains(.secondOption) {
    print("multipleOptions has SecondOption")
}
let allOptions = MyOptions(rawValue: 7)
if allOptions.contains(.thirdOption) {
    print("allOptions has ThirdOption")
}

Swift 2.0

In Swift 2.0, protocol extensions take care of most of the boilerplate for these, which are now imported as a struct that conforms to OptionSetType. (RawOptionSetType has disappeared as of Swift 2 beta 2.) The declaration is far simpler:

struct MyOptions : OptionSetType {
    let rawValue: Int

    static let None         = MyOptions(rawValue: 0)
    static let FirstOption  = MyOptions(rawValue: 1 << 0)
    static let SecondOption = MyOptions(rawValue: 1 << 1)
    static let ThirdOption  = MyOptions(rawValue: 1 << 2)
}

Now we can use set-based semantics with MyOptions:

let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = [.FirstOption, .SecondOption]
if multipleOptions.contains(.SecondOption) {
    print("multipleOptions has SecondOption")
}
let allOptions = MyOptions(rawValue: 7)
if allOptions.contains(.ThirdOption) {
    print("allOptions has ThirdOption")
}

Swift 1.2

Looking at the Objective-C options that were imported by Swift (UIViewAutoresizing, for example), we can see that options are declared as a struct that conforms to protocol RawOptionSetType, which in turn conforms to _RawOptionSetType, Equatable, RawRepresentable, BitwiseOperationsType, and NilLiteralConvertible. We can create our own like this:

struct MyOptions : RawOptionSetType {
    typealias RawValue = UInt
    private var value: UInt = 0
    init(_ value: UInt) { self.value = value }
    init(rawValue value: UInt) { self.value = value }
    init(nilLiteral: ()) { self.value = 0 }
    static var allZeros: MyOptions { return self(0) }
    static func fromMask(raw: UInt) -> MyOptions { return self(raw) }
    var rawValue: UInt { return self.value }

    static var None: MyOptions { return self(0) }
    static var FirstOption: MyOptions   { return self(1 << 0) }
    static var SecondOption: MyOptions  { return self(1 << 1) }
    static var ThirdOption: MyOptions   { return self(1 << 2) }
}

Now we can treat this new option set, MyOptions, just like described in Apple’s documentation: you can use enum-like syntax:

let opt1 = MyOptions.FirstOption
let opt2: MyOptions = .SecondOption
let opt3 = MyOptions(4)

And it also behaves like we’d expect options to behave:

let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = singleOption | .SecondOption
if multipleOptions & .SecondOption != nil {     // see note
    println("multipleOptions has SecondOption")
}
let allOptions = MyOptions.fromMask(7)   // aka .fromMask(0b111)
if allOptions & .ThirdOption != nil {
    println("allOptions has ThirdOption")
}

I’ve built a generator to create a Swift option set without all the find/replacing.

Latest: Modifications for Swift 1.1 beta 3.

Leave a Comment