A surprising number of developers struggle with bitmasks and bitwise operations. If you've had problems working with bitmasks, then I have good news for you. Swift's standard library makes this easy. In this episode, I walk you through an example I worked on last week for an application I'm working on.

Scheduling a Reminder in the Namaste Application

OptionSet Protocol

The application I'm currently working on has the ability to schedule reminders. The feature is similar to that of Apple's Clock application. A reminder or alarm can be scheduled on a specific day, but it can also be repeated on other days. A bitmask is the logical solution to solve this problem as I want to store the schedule for a reminder as a single value.

Scheduling a Timer In the Clock Application

Let me show you how easy and elegant Swift makes the implementation. Open Xcode and create a new playground.

The protocol we are interested in is the OptionSet protocol. As the name of the protocol implies, an OptionSet instance represents a set of options. This is exactly what we need for a bitmask.

I have named the type we are creating Schedule. Notice that it is a structure that conforms to the OptionSet protocol.

import Foundation

struct Schedule: OptionSet {

}

The compiler immediately warns us that Schedule doesn't conform to the RawRepresentable protocol. What is that about? Press Command and click OptionSet to navigate to the declaration of the OptionSet protocol. The protocol declaration shows us that OptionSet inherits from the RawRepresentable protocol.

public protocol OptionSet : RawRepresentable, SetAlgebra {

    associatedtype Element = Self

    init(rawValue: Self.RawValue)

}

This means the Schedule structure also needs to conform to the RawRepresentable protocol. This is easy enough, though. We declare a constant property, rawValue, of type Int.

import Foundation

struct Schedule: OptionSet {

    // MARK: - Properties

    let rawValue: Int

}

As a result, we get an initializer for free, init(rawValue:).

Creating an Option Set

We have defined an option set, but how do we work with instances of the Schedule structure? The Swift standard library comes to the rescue once again. For each option, we declare a static member. Notice that we use bitwise operators to define the raw value for each static member. Each static member has a unique value. That is key.

import Foundation

struct Schedule: OptionSet {

    // MARK: - Properties

    let rawValue: Int

    // MARK: - Options

    static let monday       = Schedule(rawValue: 1 << 0)
    static let tuesday      = Schedule(rawValue: 1 << 1)
    static let wednesday    = Schedule(rawValue: 1 << 2)
    static let thursday     = Schedule(rawValue: 1 << 3)
    static let friday       = Schedule(rawValue: 1 << 4)
    static let saturday     = Schedule(rawValue: 1 << 5)
    static let sunday       = Schedule(rawValue: 1 << 6)

}

Using this approach, the syntax may remind you of working with enumerations.

let schedule = Schedule.monday

Or

let schedule: Schedule = .monday

Working With Options Sets

Working with options sets is easy and intuitive. Take a look at the example below in which we assign the static member wednesday to a variable.

let schedule = Schedule.wednesday

Or

let schedule: Schedule = .wednesday

You can use an array to create an option set with one or more static members. It doesn't get easier than this. If you have worked with bitmasks in Objective-C, then I am sure you agree Swift's implementation is easier to work with, less confusing, and more intuitive.

let weekend = [Schedule.saturday, Schedule.sunday]

Or

let weekend: Schedule = [.saturday, .sunday]

You can also use the initializer of the Schedule struct to create an option set. This is especially useful if you've stored the raw value in, for example, a database.

let schedule = Schedule(rawValue: 127)

The rawValue property provides access to the raw value of the option set.

schedule.rawValue

Manipulating Options Sets

Creating option sets is straightforward. It doesn't stop here, though. The OptionSet protocol defines a collection of methods that make working with option sets easy and powerful. Take a look at this example in which we combine two option sets.

let schedule1: Schedule = .monday
let schedule2: Schedule = .tuesday

let union = schedule1.union(schedule2)

You can also ask an option set whether it contains a particular element.

let weekend: Schedule = [.saturday, .sunday]

if weekend.contains(.friday) {
    print("The schedule contains Friday.")
} else {
    print("The schedule doesn't contain Friday.")
}

You can use insert(_:) and remove(_:) to insert and remove members from an existing option set.

var schedule: Schedule = []

schedule.insert(.monday)
schedule.remove(.thursday)

An empty option set is represented by two square brackets.

var schedule: Schedule = []

One More Trick

You can also define static members for convenience. The example we have created in this episode makes it easy to work with schedules. We can define a few additional static members to make this even easier, more convenient.

The weekend static member defines an option set that contains the values stored in saturday and sunday. The weekdays static member only contains the days of the week.

import Foundation

struct Schedule: OptionSet {

    // MARK: - Properties

    let rawValue: Int

    // MARK: - Options

    static let monday       = Schedule(rawValue: 1 << 0)
    static let tuesday      = Schedule(rawValue: 1 << 1)
    static let wednesday    = Schedule(rawValue: 1 << 2)
    static let thursday     = Schedule(rawValue: 1 << 3)
    static let friday       = Schedule(rawValue: 1 << 4)
    static let saturday     = Schedule(rawValue: 1 << 5)
    static let sunday       = Schedule(rawValue: 1 << 6)

    static let weekend: Schedule    = [.saturday, .sunday]
    static let weekdays: Schedule   = [.monday, .tuesday, .wednesday, .thursday, .friday]

}

What's Next?

The OptionSet protocol is a wonderful member of Swift's standard library. Working with bitmasks doesn't have to be complex or cumbersome. Later this week, I show you how to create a basic user interface that allows users to define a schedule using the Schedule struct.