Blog / October 25, 2024 / 5 mins read / By Mahi Garg

OptionSet vs Enum in Swift

In Swift, both OptionSet and enum are common patterns for representing options or states. While they share some similarities, they serve different purposes and have distinct use cases. In this blog, we’ll explore OptionSet and enum in Swift, examine their unique characteristics, and look at practical examples to help clarify when to use each.

What is an OptionSet?

An OptionSet in Swift is a protocol that provides a way to represent multiple independent options or flags as a single variable using a bitwise format. OptionSet works well for cases where multiple options can be combined together, such as permissions, configurations, or state flags.

Defining an OptionSet

To create an OptionSet, define a struct that conforms to the OptionSet protocol and assign each option a unique power-of-two value so they can be combined without interference.

struct NotificationOptions: OptionSet {
    let rawValue: Int

    static let email = NotificationOptions(rawValue: 1 << 0)  // 0001
    static let sms = NotificationOptions(rawValue: 1 << 1)    // 0010
    static let push = NotificationOptions(rawValue: 1 << 2)   // 0100
    static let inApp = NotificationOptions(rawValue: 1 << 3)  // 1000

    static let all: NotificationOptions = [.email, .sms, .push, .inApp]
}

Here:

  • Each option is represented by a unique bit.
  • The options can be combined with bitwise OR (|), enabling multiple options to be stored in a single OptionSet instance.
Using OptionSet

An OptionSet allows combining and checking options efficiently:

var settings: NotificationOptions = [.email, .push]

// Check for options
print(settings.contains(.email)) // true
print(settings.contains(.sms))   // false

// Add and remove options
settings.insert(.sms)
settings.remove(.push)

print(settings.contains(.push)) // false
When to Use OptionSet
  • When multiple options can be enabled independently.
  • When options need to be combined into a single variable.
  • When options are frequently checked, added, or removed.

What is an enum?

An enum in Swift is a type that defines a group of related values. Unlike an OptionSet, an enum represents mutually exclusive cases—only one of the values can be assigned to a variable at any time.

Defining an enum

Here’s an example of an enum that represents different types of notifications:

enum NotificationType {
    case email
    case sms
    case push
    case inApp
}
Using enum

An enum is particularly useful when you need a variable to be one of several defined cases. Here’s an example:

var notification: NotificationType = .email

switch notification {
case .email:
    print("Email notification")
case .sms:
    print("SMS notification")
case .push:
    print("Push notification")
case .inApp:
    print("In-App notification")
}

With an enum, each case is unique and cannot be combined with other cases. Unlike OptionSet, assigning multiple values to a single enum variable isn’t possible because it only represents one value at a time.

When to Use enum
  • When options are mutually exclusive (only one option can be chosen at a time).
  • When each case represents a unique, distinct state.
  • When you want to use switch statements to handle different values.

OptionSet vs. Enum: Key Differences

Feature OptionSet Enum
Purpose Represents multiple, independent options Represents mutually exclusive values
Combination Options can be combined using bitwise operations Only one value at a time
Data Storage Stores options as bits, allowing multiple flags Stores only one case in a variable
Checking Options Use .contains, .insert, .remove Use switch or if statements
Common Use Cases Permissions, flags, settings State representation, event type, modes

Example Scenarios

Let’s look at a few examples to clarify when to use OptionSet vs. enum in real scenarios.

Scenario 1: File Permissions (OptionSet)

When handling file permissions like read, write, and execute, you’ll likely want to combine multiple permissions. An OptionSet is ideal for this use case.

struct FilePermissions: OptionSet {
    let rawValue: Int

    static let read = FilePermissions(rawValue: 1 << 0)     // 0001
    static let write = FilePermissions(rawValue: 1 << 1)    // 0010
    static let execute = FilePermissions(rawValue: 1 << 2)  // 0100

    static let all: FilePermissions = [.read, .write, .execute]
}

var permissions: FilePermissions = [.read, .write]
permissions.insert(.execute) // Adding execute permission

print(permissions.contains(.read))    // true
print(permissions.contains(.execute)) // true

Here, each permission is independent, so an OptionSet works perfectly to allow combining permissions efficiently.

Scenario 2: Transportation Mode (Enum)

Imagine an app where users can select a mode of transportation. Since a user can only use one mode at a time, an enum would be suitable.

enum TransportationMode {
    case walking
    case biking
    case driving
    case publicTransit
}

var mode: TransportationMode = .driving

switch mode {
case .walking:
    print("User is walking.")
case .biking:
    print("User is biking.")
case .driving:
    print("User is driving.")
case .publicTransit:
    print("User is using public transit.")
}

With an enum, each case is distinct, and only one can be assigned to mode at a time, making it ideal for mutually exclusive options.

Scenario 3: App Notification Settings (Combined Use of OptionSet and Enum)

In some cases, using both OptionSet and enum can be advantageous. Imagine an app that lets users choose the type of notifications they receive and allows combining multiple settings like enabling sound, vibration, or badge notifications. Here’s how we could use both:

enum NotificationType {
    case message
    case friendRequest
    case activity
}

struct NotificationOptions: OptionSet {
    let rawValue: Int

    static let sound = NotificationOptions(rawValue: 1 << 0)
    static let vibration = NotificationOptions(rawValue: 1 << 1)
    static let badge = NotificationOptions(rawValue: 1 << 2)
}

var notificationType: NotificationType = .message
var notificationSettings: NotificationOptions = [.sound, .vibration]

// Handling notification type
switch notificationType {
case .message:
    print("Message notification.")
case .friendRequest:
    print("Friend request notification.")
case .activity:
    print("Activity notification.")
}

// Checking notification settings
if notificationSettings.contains(.sound) {
    print("Sound enabled.")
}

if notificationSettings.contains(.badge) {
    print("Badge enabled.")
}

This example shows how enum helps define the type of notification, while OptionSet allows combining multiple settings for that notification. It’s a powerful combination for scenarios where you need both mutually exclusive options and combinable settings.

Summary

Feature OptionSet Enum
Definition Protocol for defining combinable options Type that represents a fixed set of cases
Combines Values Yes, using bitwise operations No, only one case at a time
Checking Values .contains, .insert, .remove switch or if statements
Best Use Cases Permissions, flags, multiple independent settings State representation, mutually exclusive options
Example Notification options, file permissions Transport modes, notification type

Conclusion

Choosing between OptionSet and enum depends on your requirements. If you need to represent multiple independent options that can be combined, OptionSet is the best choice. For distinct, mutually exclusive options, enum is the way to go. By understanding the key differences, you can better model your data and write more expressive, maintainable Swift code.

Comments