How to create NS_OPTIONS-style bitmask enumerations in Swift?
IosBitwise OperatorsSwiftIos Problem Overview
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?
Ios Solutions
Solution 1 - Ios
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.
Solution 2 - Ios
Xcode 6.1 Beta 2 brought some changes to the RawOptionSetType
protocol (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")
}
Solution 3 - Ios
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
Solution 4 - Ios
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!)
Solution 5 - Ios
//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`
}
Solution 6 - Ios
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.
Solution 7 - Ios
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)
Solution 8 - Ios
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
}
}
Solution 9 - Ios
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)"
Solution 10 - Ios
No one else has mentioned it -- and I kind of blundered onto it after some tinkering -- but a Swift Set
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.
Solution 11 - Ios
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)
}
Solution 12 - Ios
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"
}
}
}
Solution 13 - Ios
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.
Solution 14 - Ios
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)
}
Solution 15 - Ios
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]
}