Swift
NS_OPTIONS
bitmask
enumerations
programming

How to create NS_OPTIONS-style bitmask enumerations in Swift?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

When you need Objective-C NS_OPTIONS behavior in Swift, the right model is not a normal enum. A standard enum represents one value at a time, but bitmask flags are meant to be combined. In modern Swift, the native answer is OptionSet.

OptionSet gives you the same idea as NS_OPTIONS: each flag owns one bit, and a value can hold any combination of those bits. It is a good fit for permissions, formatting flags, feature toggles, or layout options that can be enabled together.

Use OptionSet Instead of a Plain Enum

A regular Swift enum is correct when values are mutually exclusive. If you need "read and write" at the same time, an enum is the wrong abstraction and OptionSet is the right one.

Here is the standard pattern:

swift
1struct Permissions: OptionSet {
2    let rawValue: Int
3
4    static let read    = Permissions(rawValue: 1 << 0)
5    static let write   = Permissions(rawValue: 1 << 1)
6    static let execute = Permissions(rawValue: 1 << 2)
7}

Each flag occupies a unique bit. That uniqueness is the entire point of the design. If two flags share the same bit, the program can no longer distinguish them.

Once defined, usage is straightforward:

swift
1let userPermissions: Permissions = [.read, .write]
2
3print(userPermissions.contains(.read))
4print(userPermissions.contains(.execute))

This is the Swift equivalent of combining NS_OPTIONS flags with bitwise operators, but the API feels much more natural.

Add Convenience Combinations

Many flag sets have combinations that appear often enough to deserve names. OptionSet makes that easy.

swift
1extension Permissions {
2    static let readWrite: Permissions = [.read, .write]
3    static let all: Permissions = [.read, .write, .execute]
4}
5
6let admin: Permissions = .all
7let editor: Permissions = .readWrite

These combinations improve readability because callers work with domain language instead of manually rebuilding the same bitmask each time.

If you ever need the raw integer for storage or Objective-C interop, it is still there:

swift
print(admin.rawValue)

That keeps the type safe on the Swift side while remaining compatible with APIs that still traffic in integer flags.

Understand the Historical Swift 1 Context

Older Swift versions were more awkward here, and that is why you still find discussions about NS_OPTIONS and Swift 1. Historically, developers often modeled bitmasks as structs wrapping raw values so they could preserve combination semantics.

The modern OptionSet protocol standardized that pattern and made the intent much clearer. So even if the original question comes from early Swift, the current best practice is to express the same idea with OptionSet.

The design principle has not changed:

  • use one bit per flag
  • combine flags by merging bits
  • query membership with contains

What changed is that the language now provides a clean first-class type for it.

Interoperate Cleanly with Cocoa APIs

OptionSet is especially useful when you call Apple frameworks that conceptually expect a mask of flags. On the Swift side you get a readable type, and on the Objective-C side the underlying raw value still behaves like a bitmask.

Here is a small example of a custom flag set that feels like a Cocoa-style API:

swift
1struct LayoutOptions: OptionSet {
2    let rawValue: Int
3
4    static let pinnedTop = LayoutOptions(rawValue: 1 << 0)
5    static let pinnedBottom = LayoutOptions(rawValue: 1 << 1)
6    static let centeredHorizontally = LayoutOptions(rawValue: 1 << 2)
7}
8
9func applyLayout(_ options: LayoutOptions) {
10    if options.contains(.pinnedTop) {
11        print("Pin to top")
12    }
13    if options.contains(.centeredHorizontally) {
14        print("Center horizontally")
15    }
16}
17
18applyLayout([.pinnedTop, .centeredHorizontally])

This is much easier to reason about than passing around raw integers and remembering what each bit means by hand.

Common Pitfalls

The biggest mistake is using a normal enum for values that need to be combined. That works against the type system instead of with it.

Another common mistake is assigning overlapping raw values. Every option needs its own distinct bit, usually expressed with shifts like 1 << 0, 1 << 1, and 1 << 2.

It is also easy to misuse OptionSet for values that are not really independent flags. If the data represents one exclusive mode, a normal enum is clearer.

Finally, avoid vague flag names. Bitmasks are compact, but the API should still describe real meaning, not just generic labels such as flag1 and flag2.

Summary

  • Use OptionSet for Swift equivalents of NS_OPTIONS.
  • Give each option a unique bit in the raw value.
  • Combine flags naturally with syntax such as [.read, .write].
  • Add named combinations when they improve readability.
  • Prefer a plain enum only when the values are mutually exclusive.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.