140

In Apple's documentation about interacting with C APIs, they describe the way NS_ENUM-marked C-style enumerations are imported as Swift enumerations. This makes sense, and since enumerations in Swift are readily provided as the enum value type it's easy to see how to create our own.

Further down, it says this about NS_OPTIONS-marked C-style options:

Swift also imports options marked with the NS_OPTIONS macro. Whereas options behave similarly to imported enumerations, options can also support some bitwise operations, such as &, |, and ~. In Objective-C, you represent an empty option set with the constant zero (0). In Swift, use nil to represent the absence of any options.

Given that there isn't an options value type in Swift, how can we create a C-Style options variable to work with?

jtbandes
  • 115,675
  • 35
  • 233
  • 266
Nate Cook
  • 92,417
  • 32
  • 217
  • 178
  • 3
    @Mattt's very famous "NSHipster" has an extensive description of the `RawOptionsSetType` : http://nshipster.com/rawoptionsettype/ – Klaas Sep 12 '14 at 21:14
  • Possible duplicate of [Declaring and using a bit field enum in Swift](https://stackoverflow.com/questions/24112347/declaring-and-using-a-bit-field-enum-in-swift) – Peter Ahlberg Jul 23 '17 at 19:56

15 Answers15

267

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.

Jordan Soltman
  • 3,795
  • 17
  • 31
Nate Cook
  • 92,417
  • 32
  • 217
  • 178
  • 1
    It didn't work for me unless I made `value` a `UInt32`. You also don't need to define any of the functions, relevant functions are already defined for `RawOptionSet`s (e.g. `func |(a: T, b: T) -> T`) – David Lawson Jun 08 '14 at 07:40
  • Thanks, great point about the functions -- I think the compiler was complaining about those when I didn't have the rest of the protocol conformance in place. What problems did you see with `UInt`? It's working fine for me. – Nate Cook Jun 09 '14 at 01:26
  • When using it in a playground with UInt I got "fatal error: Can't unwrap Optional.None", but it worked fine with UInt32. Really weird. Anyway, I managed to use classes to take care of all the required functions for a slightly cleaner implementation: http://www.davidlawson.com.au/2014/06/implementing-ns_options-in-swift/ – David Lawson Jun 09 '14 at 05:05
  • In beta 5, `RawOptionSet` has been renamed to `RawOptionSetType` and instead of `func getLogicValue() -> Bool` it should now be `var boolValue:Bool { get { return self.value != 0} }` – Dorian Roy Aug 05 '14 at 14:30
  • Thanks for the heads up! Updated the answer above. – Nate Cook Aug 05 '14 at 15:31
  • beta 6 adds the need to implement BitwiseOperationsType. – Klaas Aug 19 '14 at 18:24
  • @Klaas Grab that code again - Apple dropped `BooleanType` conformance for some reason, so standard bitmask checks aren't working. – Nate Cook Aug 19 '14 at 18:45
  • @NateCook, how do you know which functions to implement? I have been looking for the BitwiseOperationsType protocol but can't find it anywhere. Please enlighten me. – user965972 Aug 21 '14 at 14:55
  • @user965972 Do you know how to view the Swift "header file"? You can follow [these instructions](http://ericasadun.com/2014/08/20/swift-xcode-beta-6-accessing-swift-native-definitions/) to get there, then just do a search. – Nate Cook Aug 21 '14 at 19:06
  • Thanks @NateCook, should have thought of that but the documentation is so good I usually never bother to look at the header files. Didn't cross my mind, D'Oh. – user965972 Aug 21 '14 at 22:58
  • Thanks @NateCook, I suggest add in struct `func has(option: MyOptions) -> Bool { return self & option != nil }` – Maksim Bolshakov Mar 20 '15 at 10:59
  • There is also a much more readable way of initialising your bitmask values. It is available in the Swift Standard Library Reference [here](https://developer.apple.com/library/prerelease/mac/documentation/Swift/Reference/Swift_OptionSetType_Protocol/index.html), which may have been updated since this answer was posted. – Matthew Quiros Jul 13 '15 at 11:08
  • @MattQuiros Yep, that's a much better way - however, it's only available as part of Swift 2.0. – Craig Otis Jul 15 '15 at 11:14
  • 2
    Is there a solution that uses enum instead of struct? I need mine to be compatible with objective-c... – jowie Dec 04 '15 at 15:25
  • 1
    @jowie `enum CollisionTypes: UInt32 { case Player = 1 case Wall = 2 case Star = 4 case Vortex = 8 case Finish = 16 }` – mccoyLBI Mar 15 '16 at 18:58
  • How can I get the raw value from a `MyOptions` array? In the example `let multipleOptions: MyOptions = [.FirstOption, .SecondOption]` I wish to get 3 from `multipleOptions`. Right now I am writing my own function to do this. – Franklin Yu May 12 '16 at 06:57
  • @FranklinYu: Use the option set's `rawValue` property. – Nate Cook May 12 '16 at 19:15
  • @NateCook, thank you. I forgot to cast the `MyOptions` array back to `MyOptions`, since an array does not have a `rawValue` property. – Franklin Yu May 12 '16 at 22:43
  • Great answer, this really helped me. in the swift 2.0 section, are you missing this part? init(rawValue: Int) { self.rawValue = rawValue } Or is it not needed because it is a struct? – Chris Jun 07 '16 at 15:05
  • @cmarti1138 Right, that init gets added automatically. However, if you add any other initializers you'll need to add it manually. – Nate Cook Jun 07 '16 at 16:21
  • 1
    In this instance, [Apple's docs](https://developer.apple.com/documentation/swift/optionset) are really good. – Mr Rogers Sep 28 '17 at 16:44
12

Xcode 6.1 Beta 2 brought some changes to the RawOptionSetTypeprotocol (see this Airspeedvelocity blog entry and the Apple release notes).

Based on Nate Cooks example here is an updated solution. You can define your own option set like this:

struct MyOptions : RawOptionSetType, BooleanType {
    private var value: UInt
    init(_ rawValue: UInt) { self.value = rawValue }

    // MARK: _RawOptionSetType
    init(rawValue: UInt) { self.value = rawValue }

    // MARK: NilLiteralConvertible
    init(nilLiteral: ()) { self.value = 0}

    // MARK: RawRepresentable
    var rawValue: UInt { return self.value }

    // MARK: BooleanType
    var boolValue: Bool { return self.value != 0 }

    // MARK: BitwiseOperationsType
    static var allZeros: MyOptions { return self(0) }

    // MARK: User defined bit values
    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) }
    static var All: MyOptions           { return self(0b111) }
}

It can then be used like this to define variables:

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

And like this to test for bits:

let singleOption = MyOptions.FirstOption
let multipleOptions: MyOptions = singleOption | .SecondOption
if multipleOptions & .SecondOption {
    println("multipleOptions has SecondOption")
}

let allOptions = MyOptions.All
if allOptions & .ThirdOption {
    println("allOptions has ThirdOption")
}
Klaas
  • 22,394
  • 11
  • 96
  • 107
8

Swift 2.0 example from the documentation:

struct PackagingOptions : OptionSetType {
    let rawValue: Int
    init(rawValue: Int) { self.rawValue = rawValue }

    static let Box = PackagingOptions(rawValue: 1)
    static let Carton = PackagingOptions(rawValue: 2)
    static let Bag = PackagingOptions(rawValue: 4)
    static let Satchel = PackagingOptions(rawValue: 8)
    static let BoxOrBag: PackagingOptions = [Box, Bag]
    static let BoxOrCartonOrBag: PackagingOptions = [Box, Carton, Bag]
}

You can find it here

Tomasz Bąk
  • 6,124
  • 3
  • 34
  • 48
6

In Swift 2 (currently beta as part of the Xcode 7 beta), NS_OPTIONS-style types are imported as subtypes of the new OptionSetType type. And thanks to the new Protocol Extensions feature and the way OptionSetType is implemented in the standard library, you can declare your own types that extend OptionsSetType and get all the same functions and methods that imported NS_OPTIONS-style types get.

But those functions aren't based on bitwise arithmetic operators anymore. That working with a set of non-exclusive Boolean options in C requires masking and twiddling bits in a field is an implementation detail. Really, a set of options is a set... a collection of unique items. So OptionsSetType gets all the methods from the SetAlgebraType protocol, like creation from array literal syntax, queries like contains, masking with intersection, etc. (No more having to remember which funny character to use for which membership test!)

rickster
  • 124,678
  • 26
  • 272
  • 326
5
//Swift 2.0
 //create
    struct Direction : OptionSetType {
        let rawValue: Int
        static let None   = Direction(rawValue: 0)
        static let Top    = Direction(rawValue: 1 << 0)
        static let Bottom = Direction(rawValue: 1 << 1)
        static let Left   = Direction(rawValue: 1 << 2)
        static let Right  = Direction(rawValue: 1 << 3)
    }
//declare
var direction: Direction = Direction.None
//using
direction.insert(Direction.Right)
//check
if direction.contains(.Right) {
    //`enter code here`
}
PhuocLuong
  • 699
  • 9
  • 18
4

If you don't need to interoperate with Objective-C and just want the surface semantics of bit masks in Swift, I've written a simple "library" called BitwiseOptions that can do this with regular Swift enumerations, e.g.:

enum Animal: BitwiseOptionsType {
    case Chicken
    case Cow
    case Goat
    static let allOptions = [.Chicken, .Cow, .Goat]
}

var animals = Animal.Chicken | Animal.Goat
animals ^= .Goat
if animals & .Chicken == .Chicken {
    println("Chick-Fil-A!")
}

and so on. No actual bits are being flipped here. These are set operations on opaque values. You can find the gist here.

Gregory Higley
  • 15,923
  • 9
  • 67
  • 96
2

If the only functionality we are needing is a way to combine options with | and check if combined options contain a particular option with & an alternative to Nate Cook's answer could be this:

Create an options protocol and overload | and &:

protocol OptionsProtocol {

    var value: UInt { get }
    init (_ value: UInt)

}

func | <T: OptionsProtocol>(left: T, right: T) -> T {
    return T(left.value | right.value)
}

func & <T: OptionsProtocol>(left: T, right: T) -> Bool {
    if right.value == 0 {
        return left.value == 0
    }
    else {
        return left.value & right.value == right.value
    }
}

Now we can create options structs more simply like so:

struct MyOptions: OptionsProtocol {

    private(set) var value: UInt
    init (_ val: UInt) {value = val}

    static var None: MyOptions { return self(0) }
    static var One: MyOptions { return self(1 << 0) }
    static var Two: MyOptions { return self(1 << 1) }
    static var Three: MyOptions { return self(1 << 2) }
}

They can be used as follows:

func myMethod(#options: MyOptions) {
    if options & .One {
        // Do something
    }
}

myMethod(options: .One | .Three) 
Simple99
  • 1,038
  • 11
  • 20
2

As Rickster already mentioned, you can use OptionSetType in Swift 2.0. NS_OPTIONS types get imported as conforming to the OptionSetType protocol, which presents a set-like interface for options:

struct CoffeeManipulators : OptionSetType {
    let rawValue: Int
    static let Milk     = CoffeeManipulators(rawValue: 1)
    static let Sugar    = CoffeeManipulators(rawValue: 2)
    static let MilkAndSugar = [Milk, Sugar]
}

It gives you this way of working:

struct Coffee {
    let manipulators:[CoffeeManipulators]

    // You can now simply check if an option is used with contains
    func hasMilk() -> Bool {
        return manipulators.contains(.Milk)
    }

    func hasManipulators() -> Bool {
        return manipulators.count != 0
    }
}
Antoine
  • 23,526
  • 11
  • 88
  • 94
2

Just posting an extra example for anyone else who was wondering if you could combine compound options. You can, and they combine like you'd expect if you're used to good old bitfields:

struct State: OptionSetType {
    let rawValue: Int
    static let A      = State(rawValue: 1 << 0)
    static let B      = State(rawValue: 1 << 1)
    static let X      = State(rawValue: 1 << 2)

    static let AB:State  = [.A, .B]
    static let ABX:State = [.AB, .X]    // Combine compound state with .X
}

let state: State = .ABX
state.contains(.A)        // true
state.contains(.AB)       // true

It flattens the set [.AB, .X] into [.A, .B, .X] (at least semantically):

print(state)      // 0b111 as expected: "State(rawValue: 7)"
print(State.AB)   // 0b11 as expected: "State(rawValue: 3)"
sleep
  • 4,855
  • 5
  • 34
  • 51
1

No one else has mentioned it -- and I kind of blundered onto it after some tinkering -- but a Swift Set seems to work fairly well.

If we think (maybe to a Venn diagram?) about what a bit mask is actually representing, it is a possibly empty set.

Of course, in approaching the problem from first principles, we lose the convenience of bitwise operators, but gain powerful set-based methods which improves readability.

Here is my tinkering for example:

enum Toppings : String {
    // Just strings 'cause there's no other way to get the raw name that I know of...
    // Could be 1 << x too...
    case Tomato = "tomato"
    case Salami = "salami"
    case Cheese = "cheese"
    case Chicken = "chicken"
    case Beef = "beef"
    case Anchovies = "anchovies"

    static let AllOptions: Set<Toppings> = [.Tomato, .Salami, .Cheese, .Chicken, .Anchovies, .Beef]
}

func checkPizza(toppings: Set<Toppings>) {
    if toppings.contains(.Cheese) {
        print("Possible dairy allergies?")
    }

    let meats: Set<Toppings> = [.Beef, .Chicken, .Salami]
    if toppings.isDisjointWith(meats) {
        print("Vego-safe!")
    }
    if toppings.intersect(meats).count > 1 {
        print("Limit one meat, or 50¢ extra charge!")
    }

    if toppings == [Toppings.Cheese] {
        print("A bit boring?")
    }
}

checkPizza([.Tomato, .Cheese, .Chicken, .Beef])

checkPizza([.Cheese])

I find this nice because I feel it comes from a first principles approach to the problem -- much like Swift -- rather than trying to adapt C-style solutions.

Would also like to hear some Obj-C use cases that would challenge this different paradigm, where the integer raw values still shows merit.

BugSpray
  • 113
  • 9
1

In order to avoid hard coding the bit positions, which is unavoidable when using (1 << 0), (1 << 1), (1 << 15) etc. or even worse 1, 2, 16384 etc. or some hexadecimal variation, one could first defines the bits in an enum, then let said enum do the bit ordinal calculation:

// Bits
enum Options : UInt {
    case firstOption
    case secondOption
    case thirdOption
}

// Byte
struct MyOptions : OptionSet {
    let rawValue: UInt

    static let firstOption  = MyOptions(rawValue: 1 << Options.firstOption.rawValue)
    static let secondOption = MyOptions(rawValue: 1 << Options.secondOption.rawValue)
    static let thirdOption  = MyOptions(rawValue: 1 << Options.thirdOption.rawValue)
}
SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
1

I use the following I need the both values I can get, rawValue for indexing arrays and value for flags.

enum MyEnum: Int {
    case one
    case two
    case four
    case eight

    var value: UInt8 {
        return UInt8(1 << self.rawValue)
    }
}

let flags: UInt8 = MyEnum.one.value ^ MyEnum.eight.value

(flags & MyEnum.eight.value) > 0 // true
(flags & MyEnum.four.value) > 0  // false
(flags & MyEnum.two.value) > 0   // false
(flags & MyEnum.one.value) > 0   // true

MyEnum.eight.rawValue // 3
MyEnum.four.rawValue  // 2

And if one needs more just add a computed property.

enum MyEnum: Int {
    case one
    case two
    case four
    case eight

    var value: UInt8 {
        return UInt8(1 << self.rawValue)
    }

    var string: String {
        switch self {
        case .one:
            return "one"
        case .two:
            return "two"
        case .four:
            return "four"
        case .eight:
            return "eight"
        }
    }
}
Peter Ahlberg
  • 1,359
  • 2
  • 24
  • 26
1

re: Sandbox and bookmark creations using option sets with several options

let options:NSURL.BookmarkCreationOptions = [.withSecurityScope,.securityScopeAllowOnlyReadAccess]
let temp = try link.bookmarkData(options: options, includingResourceValuesForKeys: nil, relativeTo: nil)

solution to needing to combine options for creations, useful when not all options are mutually exclusive.

slashlos
  • 913
  • 9
  • 17
0

Nate's answer is good but I would make it DIY, like so:

struct MyOptions : OptionSetType {
    let rawValue: Int

    static let None         = Element(rawValue: 0)
    static let FirstOption  = Element(rawValue: 1 << 0)
    static let SecondOption = Element(rawValue: 1 << 1)
    static let ThirdOption  = Element(rawValue: 1 << 2)
}
Community
  • 1
  • 1
Ethan
  • 18,584
  • 15
  • 51
  • 72
0

Use an Option Set Type, in swift 3 use OptionSet

struct ShippingOptions: OptionSet {
    let rawValue: Int

    static let nextDay    = ShippingOptions(rawValue: 1 << 0)
    static let secondDay  = ShippingOptions(rawValue: 1 << 1)
    static let priority   = ShippingOptions(rawValue: 1 << 2)
    static let standard   = ShippingOptions(rawValue: 1 << 3)

    static let express: ShippingOptions = [.nextDay, .secondDay]
    static let all: ShippingOptions = [.express, .priority, .standard]
}
geek1706
  • 980
  • 9
  • 16
  • 1
    This is more or less already covered in [this answer](https://stackoverflow.com/a/24066171). – Pang Jun 06 '17 at 03:14