How to Create Ns_Options-Style Bitmask Enumerations in Swift

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.

Objective-C NS_OPTIONS in Swift

Your syntax is simply wrong. The Swift syntax for a bit field (which is actually called an OptionSet in Swift) is the array syntax, not the bitwise OR syntax.

Objective-C enums created using NS_OPTIONS are automatically imported into Swift as OptionSets.

So you just need to do let default: MyType = [.option1, .option2] instead of trying to replicate the Obj-C bitfield syntax.

For more info on the topic, see How to create NS_OPTIONS-style bitmask enumerations in Swift?

How to define category bit mask enumeration for SpriteKit in Swift?

What you could do is use the binary literals: 0b1, 0b10, 0b100, etc.

However, in Swift you cannot bitwise-OR enums, so there is really no point in using bitmasks in enums. Check out this question for a replacement for NS_OPTION.

NS_OPTIONS Bitmask Autogeneration

You can use a technique called X Macro

#define VALUES \
VALUE_LINE(Value1) \
VALUE_LINE(Value2) \
VALUE_LINE(Value3)

typedef NS_ENUM(NSUInteger, ExampleEnum)
{
#define VALUE_LINE(x) x,
VALUES
#undef VALUE_LINE
}

typedef NS_OPTIONS(NSUInteger, ExampleEnumFlags)
{
#define VALUE_LINE(x) x##Flag = 1 << x,
VALUES
#undef VALUE_LINE
}

Declaring and using a bit field enum in Swift

Updated for Swift 2/3

Since swift 2, a new solution has been added as "raw option set" (see: Documentation), which is essentially the same as my original response, but using structs that allow arbitrary values.

This is the original question rewritten as an OptionSet:

struct MyOptions: OptionSet
{
let rawValue: UInt8

static let One = MyOptions(rawValue: 0x01)
static let Two = MyOptions(rawValue: 0x02)
static let Four = MyOptions(rawValue: 0x04)
static let Eight = MyOptions(rawValue: 0x08)
}

let m1 : MyOptions = .One

let combined : MyOptions = [MyOptions.One, MyOptions.Four]

Combining with new values can be done exactly as Set operations (thus the OptionSet part), .union, likewise:

m1.union(.Four).rawValue // Produces 5

Same as doing One | Four in its C-equivalent. As for One & Mask != 0, can be specified as a non-empty intersection

// Equivalent of A & B != 0
if !m1.intersection(combined).isEmpty
{
// m1 belongs is in combined
}

Weirdly enough, most of the C-style bitwise enums have been converted to their OptionSet equivalent on Swift 3, but Calendar.Compontents does away with a Set:

let compontentKeys : Set = [.day, .month, .year]

Whereas the original NSCalendarUnit was a bitwise enum. So both approaches are usable (thus the original response remains valid)

Original Response

I think the best thing to do, is to simply avoid the bitmask syntax until the Swift devs figure out a better way.

Most of the times, the problem can be solved using an enum and and a Set

enum Options
{
case A, B, C, D
}

var options = Set(arrayLiteral: .A, .D)

An and check (options & .A) could be defined as:

options.contains(.A)

Or for multiple "flags" could be:

options.isSupersetOf(Set(arrayLiteral: .A, .D))

Adding new flags (options |= .C):

options.insert(.C)

This also allows for using all the new stuff with enums: custom types, pattern matching with switch case, etc.

Of course, it doesn't have the efficiency of bitwise operations, nor it would be compatible with low level things (like sending bluetooth commands), but it's useful for UI elements that the overhead of the UI outweighs the cost of the Set operations.

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.

Options Enum Equivalent in Swift

This is known as an option set, bridged automatically from an Objective C NS_OPTION. They are effectively a more type-safe way of handling an enum type in which multiple options can be selected. In Swift, these values are not treated like bit masks (as in Objective C and hence why the | operator is not valid here) but rather as distinct values of an enum. An option set effectively acts like a Set of that enum, providing a type safe bridge with equivalent functionality.

Apple specifies the details of their implementation here:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html

Swift. Bit mask of weekdays issues. Switch bits

In your case, consider adopting OptionSetType.

With declaring a struct conforming to OptionSetType:

(Assuming you are still using Swift 2.)

struct Days: OptionSetType {
var rawValue: Int
init(rawValue: Int) {self.rawValue = rawValue}

static let Monday = Days(rawValue: 1<<0)
static let Tuesday = Days(rawValue: 1<<1)
static let Wednesday = Days(rawValue: 1<<2)
static let Thursday = Days(rawValue: 1<<3)
static let Friday = Days(rawValue: 1<<4)
static let Saturday = Days(rawValue: 1<<5)
static let Sunday = Days(rawValue: 1<<6)
}

You can write something like this:

func markDaysAsSelectedWith(days: Days)
{
if days.contains(.Monday) {
print("Monday marked")
}
if days.contains(.Tuesday) {
print("Tuesday marked")
}
if days.contains(.Wednesday) {
print("Wednesday marked")
}
if days.contains(.Thursday) {
print("Thursday marked")
}
if days.contains(.Friday) {
print("Friday marked")
}
if days.contains(.Saturday) {
print("Saturday marked")
}
if days.contains(.Sunday) {
print("Sunday marked")
}
}

And use it as:

markDaysAsSelectedWith([.Monday,.Tuesday])
//Output:
//Monday marked
//Tuesday marked
let number = 12
let days = Days(rawValue: number)
markDaysAsSelectedWith(days)
//Output:
//Wednesday marked
//Thursday marked

(ADDITION)how to get number from selected days

As already noted in Martin R's comment, you can retrieve underlying rawValue with rawValue property:

let otherDays: Days = [.Wednesday,.Thursday]
print(otherDays.rawValue) //->12

NS_OPTIONS matches

The correct way to check for this value is to first bitwise AND the values and then check for equality to the required value.

MyCellCorners cellCorners = MyCellCornerTopLeft | MyCellCornerTopRight;

if ((cellCorners & MyCellCornerTopLeft) == MyCellCornerTopLeft) {
// top left corner set
}

The following reference explains why this is correct and provides other insights into enumerated types.

Reference: checking-for-a-value-in-a-bit-mask

Create type of struct by assignment instead of initialization

Yep.

extension MyType: ExpressibleByIntegerLiteral {
init(integerLiteral: Int) { self.value = integerLiteral }
}

See:

  • https://developer.apple.com/documentation/swift/swift_standard_library/initialization_with_literals
  • and https://nshipster.com/swift-literals/


Related Topics



Leave a reply



Submit