13. How to use OptionSet Protocol?

It will take about 3 minutes to finish reading this article.

1. NS_OPTIONS and NS_ENUM in OC

The following enumeration definitions are common in Objective-C:

1
2
3
4
5
6
7

typedef NS_ENUM(NSInteger, CustomEnum) {
CustomEnumOption1,
CustomEnumOption2,
CustomEnumOption3
};

1
2
3
4
5
6
7
8

typedef NS_OPTIONS(NSUInteger, CustomOptions) {
CustomOptionNone = 0,
CustomOptionOption1 = 1 << 0,
CustomOptionOption2 = 1 << 1,
CustomOptionOption3 = 1 << 2
};

Their differences:

NS_ENUM: used to define a set of related discrete values that cannot be combined by bit operations.

NS_OPTIONS: Used to define a set of discrete values that can be combined by bit operations, usually used to represent a set of options or flags.

Here is an example of using NS_OPTIONS:

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
30
31
32
33
34
35
36

typedef NS_OPTIONS(NSUInteger, CustomOptions) {
CustomOptionNone = 0,
CustomOptionOption1 = 1 << 0,
CustomOptionOption2 = 1 << 1,
CustomOptionOption3 = 1 << 2
};

@interface CustomOptionHandler : NSObject

// Add method to execute code associated with option
+ (void)performActionForOption:(CustomOptions)option;

@end

@implementation CustomOptionHandler

+ (void)performActionForOption:(CustomOptions)option {
if (option & CustomOptionOption1) {
//Execute the code related to CustomOptionOption1
NSLog(@"Option 1 is selected.");
}

if (option & CustomOptionOption2) {
//Execute code related to CustomOptionOption2
NSLog(@"Option 2 is selected.");
}

if (option & CustomOptionOption3) {
//Execute code related to CustomOptionOption3
NSLog(@"Option 3 is selected.");
}
}

@end

The calling code is as follows:

1
2
3
4

CustomOptions selectedOptions = CustomOptionOption1 | CustomOptionOption3;
[CustomOptionHandler performActionForOption:selectedOptions];

Results of the:

1
2
3
Option 1 is selected.
Option 3 is selected.

2. OptionSet protocol

OptionSet is the Swift version of NS_OPTIONS, but it is not a simple replacement, it is more complex.

In Swift, an OptionSet is a protocol used to represent a set of options or a collection of flags. It is a type-safe way to manage bit masks with discrete values. The OptionSet protocol allows you to define a set of options with a bit mask and operate in a set-like manner. Some types in the Swift standard library, such as NSOptions, UIControlEvents, etc., implement the OptionSet protocol.

1
2
3
4
5
public protocol OptionSet: RawRepresentable, SetAlgebra {
associatedtype Element = Self
init(rawValue: Self.RawValue)
}

(1) The OptionSet protocol declares a public access level, indicating that it is publicly visible.

(2) The OptionSet protocol inherits two protocols: RawRepresentable and SetAlgebra. This means that any type that conforms to the OptionSet protocol must satisfy the requirements of both protocols.

(3) associatedtype Element = Self: This section specifies the associated type Element in the OptionSet protocol and sets it to Self by default, which means that the type of Element will be the type itself that follows the OptionSet protocol.

(4) init(rawValue: Self.RawValue): This is a declaration of an initialization method that allows the creation of an OptionSet instance from a raw value. This method must be implemented.

Next is an extension to the OptionSet protocol, which provides some default implementations:

1
2
3
4
5
6
extension OptionSet {
@inlinable public func union(_ other: Self) -> Self
@inlinable public func intersection(_ other: Self) -> Self
@inlinable public func symmetricDifference(_ other: Self) -> Self
}

These extensions provide default set operation methods for types that follow the OptionSet protocol, including union, intersection, and symmetric difference. These methods are useful when combining options using bit masks, and they allow you to easily perform various operations such as adding, removing, or checking options.

3. Sample code

Let’s take a simple example to understand OptionSet in depth.

(1) First, we define a theme option set that follows the OptionSet protocol.

1
2
3
4
5
6
7
8
9
struct ThemeOptions: OptionSet {
let rawValue: Int

static let dark = ThemeOptions(rawValue: 1 << 0)
static let light = ThemeOptions(rawValue: 1 << 1)
static let blue = ThemeOptions(rawValue: 1 << 2)
static let green = ThemeOptions(rawValue: 1 << 3)
}

We create a structure called ThemeOptions, which conforms to the OptionSet protocol. We define four theme options: dark, light, blue, and green, and assign a bitmask value to each option.

(2) Users choose different themes:

1
var selectedOptions: ThemeOptions = [.dark, .blue]

(3) Use the union method to add more options:

1
selectedOptions = selectedOptions.union(.green)

(4) To check whether an option has been selected, you can use the contains method:

1
2
3
4
5
6
if selectedOptions.contains(.light) {
print("Light theme is selected.")
} else {
print("Light theme is not selected.")
}