Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Win a Let's visionOS 2024 conference ticket. Join for free

OptionSet in Swift explained with code examples

OptionSet in Swift allows you to define a set of options for configurations. It’s the Swift variant of the well-known NS_OPTIONS in Objective-C and it’s used throughout the standard libraries.

A set of options is often confused by a set of enum cases, but they’re not the same. While you could create a similar solution using enums, they’re meant to be used singularly. You can learn more about enums in my article Enum explained in-depth with examples in Swift. On the contrary, an OptionSet allows you to pass in multiple values that all get applied as a configuration.

What is an OptionSet?

The OptionSet protocol allows you to define available configuration options for a specific type or method. It’s commonly used in the standard libraries, for example, when parsing JSON in Swift:

data = try JSONSerialization.data(withJSONObject: messageWrapper, options: [.prettyPrinted, .sortedKeys])

In the above example, we passed in the options prettyPrinted and sortedKeys. The output will be pretty printed JSON data with keys sorted alphabetically.

The OptionSet protocol is a type that represents bitset types, where the combination of cases results in individual bits. To demonstrate, we can print out the raw values of the above options:

print([.prettyPrinted]) // RawValue 1
print([.sortedKeys]) // RawValue 2
print([.sortedKeys, .prettyPrinted]) // RawValue 3
print([.sortedKeys, .prettyPrinted, .fragmentsAllowed]) // RawValue 7
print([.sortedKeys, .prettyPrinted, .fragmentsAllowed, .withoutEscapingSlashes]) // RawValue 15
print([.sortedKeys, .prettyPrinted, .withoutEscapingSlashes]) // RawValue 11

The raw outcome value matches the sum of individual raw values:

struct WritingOptions: OptionSet {

    init(rawValue: UInt)
    static var prettyPrinted: JSONSerialization.WritingOptions // RawValue 1
    static var sortedKeys: JSONSerialization.WritingOptions // RawValue 2
    static var fragmentsAllowed: JSONSerialization.WritingOptions // RawValue 4
    static var withoutEscapingSlashes: JSONSerialization.WritingOptions // RawValue 8
}

Now that we know how the underlying type works, it’s time to create our options.

How to create an OptionSet

You can create a custom OptionSet by defining a new type conforming to the protocol:

struct UploadOptions: OptionSet {
    let rawValue: UInt

    static let waitsForConnectivity    = UploadOptions(rawValue: 1 << 0)
    static let allowCellular  = UploadOptions(rawValue: 1 << 1)
    static let multipathTCPAllowed   = UploadOptions(rawValue: 1 << 2)

    static let standard: UploadOptions = [.waitsForConnectivity, .allowCellular]
    static let all: UploadOptions = [.waitsForConnectivity, .allowCellular, .multipathTCPAllowed]
}

You’ll have to define the associated type rawValue to indicate the type used for comparison. I recommend using an unsigned integer since that can only contain positive integers.

Each defined option uses a raw value using the bitwise left shift operator (<<). This operator moves all bits in a number to the left by a certain number of places. In other words:

  • 1 << 0 equals no move, so the outcome is 1
  • 1 << 1 equals a move by 1, so the next bit is 2
  • 1 << 2 equals a move by 2, so the raw value will be not 1, not 2, but 4
  • etc.

Finally, we defined a standard set of options and a static property containing all available options.

Reading values from an OptionSet

After creating a set of options, it’s time to read the configured options inside a method. In our case, we’re going to create an uploader that makes use of the set of options:

struct Uploader {
    private let urlSession: URLSession

    init(options: UploadOptions) {
        let configuration = URLSessionConfiguration.default

        if options.contains(.multipathTCPAllowed) {
            configuration.multipathServiceType = .handover
        }

        configuration.allowsCellularAccess = options.contains(.allowCellular)
        configuration.waitsForConnectivity = options.contains(.waitsForConnectivity)

        urlSession = URLSession(configuration: configuration)
    }
}

As you can see, we created a convenience way to configure the underlying URLSession of our uploader:

let uploader = Uploader(options: [.waitsForConnectivity, .allowCellular])

Conclusion

OptionSet in Swift allows you to define a set of configurations with a method or type initializer. Throughout the standard libraries, you can find examples of option sets to configure types like a JSONSerializer. By providing available options, you allow implementors of a type to configure instances in a readable manner without exposing too many implementation details.

If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.

Thanks!

 

Do you know everything about Swift?

You don't need to master everything, but staying informed is crucial. Join our community of 17,905 developers and stay ahead of the curve:


Featured SwiftLee Jobs

Find your next Swift career step at world-class companies with impressive apps by joining the SwiftLee Talent Collective. I'll match engineers in my collective with exciting app development companies. SwiftLee Jobs