Skip to content

Enumerations

Enumerations (or enums) are a way to define a custom type with a fixed set of related values. They help you work with these values safely and clearly in your code.

Unlike some languages where enums are just named integers, Swift enums are more powerful. They don't need to have values for every case, and those values can be strings, characters, integers, or floats. Enums can also store extra data (associated values) of any type with each case, similar to variants in other languages.

Swift enums are full types, like classes. They can have computed properties, methods, initializers, extensions, and follow protocols.

How to Define an Enum

Use the enum keyword, followed by the name and curly braces for the definition:

swift
enum ExampleEnum {
    // cases go here
}

Example for basic directions:

swift
enum Direction {
    case up
    case down
    case left
    case right
}

Each value (like up) is an enum case. Use case to add new ones. Cases are unique values of the enum type itself.

You can list multiple cases on one line:

swift
enum Color {
    case red, green, blue, yellow
}

Enum names start with a capital letter and are singular (e.g., Direction, not Directions).

To use it:

swift
var currentDirection = Direction.left
currentDirection = .right  // Shorter syntax once type is known

Matching Enum Values with Switch

Use a switch statement to handle different cases:

swift
currentDirection = .down
switch currentDirection {
case .up:
    print("Going higher")
case .down:
    print("Heading lower")
case .left:
    print("Turn sideways")
case .right:
    print("Opposite side")
}
// Prints "Heading lower"

Switches must cover all cases (exhaustive). If not, use default:

swift
let favoriteColor = Color.blue
switch favoriteColor {
case .blue:
    print("Calm and cool")
default:
    print("Other shades are fine too")
}
// Prints "Calm and cool"

Listing All Cases

To get all cases, make the enum conform to CaseIterable. It adds an allCases property:

swift
enum Fruit: CaseIterable {
    case apple, banana, orange
}

let totalFruits = Fruit.allCases.count
print("\(totalFruits) options")
// Prints "3 options"

for fruit in Fruit.allCases {
    print(fruit)
}
// apple
// banana
// orange

Associated Values

Enums can store extra data with cases, and each case can have different types:

Example for item identifiers:

swift
enum Identifier {
    case numeric(Int, Int)
    case textual(String)
}

This means numeric holds two ints, textual holds a string.

Usage:

swift
var itemID = Identifier.numeric(123, 456)
itemID = .textual("ABC123")

Extract in switch:

swift
switch itemID {
case .numeric(let part1, let part2):
    print("Numeric: \(part1)-\(part2)")
case .textual(let code):
    print("Text: \(code)")
}
// Prints "Text: ABC123"

Or use single let/var:

swift
switch itemID {
case let .numeric(part1, part2):
    print("Numeric: \(part1)-\(part2)")
case let .textual(code):
    print("Text: \(code)")
}

For single-case checks, use if case:

swift
if case .textual(let code) = itemID {
    print("Text: \(code)")
}

Raw Values

Cases can have default (raw) values, all of the same type:

swift
enum Symbol: Character {
    case plus = "+"
    case minus = "-"
    case multiply = "*"
}

Raw values are fixed per case.

Automatic Raw Values

For ints, they increment from 0 (or specified start):

swift
enum Level: Int {
    case beginner = 1
    case intermediate
    case advanced
}
// beginner: 1, intermediate: 2, advanced: 3

For strings, it's the case name:

swift
enum Shape: String {
    case circle, square, triangle
}
// circle: "circle", etc.

Access with rawValue:

swift
let midLevel = Level.intermediate.rawValue  // 2
let roundShape = Shape.circle.rawValue  // "circle"

Creating from Raw Value

Enums with raw values get a failable initializer:

swift
let maybeLevel = Level(rawValue: 2)  // .intermediate (optional)
let invalid = Level(rawValue: 5)  // nil

Example check:

swift
let searchLevel = 3
if let foundLevel = Level(rawValue: searchLevel) {
    print("Found: \(foundLevel)")
} else {
    print("No level at \(searchLevel)")
}
// Prints "Found: advanced"

Recursive Enums

Enums can reference themselves in associated values (for nested data). Use indirect:

swift
enum MathOp {
    case value(Int)
    indirect case add(MathOp, MathOp)
    indirect case subtract(MathOp, MathOp)
}

Or indirect for the whole enum:

swift
indirect enum MathOp {
    case value(Int)
    case add(MathOp, MathOp)
    case subtract(MathOp, MathOp)
}

Example: 10 + (5 - 3)

swift
let ten = MathOp.value(10)
let five = MathOp.value(5)
let three = MathOp.value(3)
let diff = MathOp.subtract(five, three)
let result = MathOp.add(ten, diff)

Evaluate recursively:

swift
func calculate(_ op: MathOp) -> Int {
    switch op {
    case .value(let num):
        return num
    case .add(let left, let right):
        return calculate(left) + calculate(right)
    case .subtract(let left, let right):
        return calculate(left) - calculate(right)
    }
}

print(calculate(result))  // 12

Released under the MIT License.