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:
enum ExampleEnum {
// cases go here
}Example for basic directions:
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:
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:
var currentDirection = Direction.left
currentDirection = .right // Shorter syntax once type is knownMatching Enum Values with Switch
Use a switch statement to handle different cases:
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:
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:
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
// orangeAssociated Values
Enums can store extra data with cases, and each case can have different types:
Example for item identifiers:
enum Identifier {
case numeric(Int, Int)
case textual(String)
}This means numeric holds two ints, textual holds a string.
Usage:
var itemID = Identifier.numeric(123, 456)
itemID = .textual("ABC123")Extract in switch:
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:
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:
if case .textual(let code) = itemID {
print("Text: \(code)")
}Raw Values
Cases can have default (raw) values, all of the same type:
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):
enum Level: Int {
case beginner = 1
case intermediate
case advanced
}
// beginner: 1, intermediate: 2, advanced: 3For strings, it's the case name:
enum Shape: String {
case circle, square, triangle
}
// circle: "circle", etc.Access with rawValue:
let midLevel = Level.intermediate.rawValue // 2
let roundShape = Shape.circle.rawValue // "circle"Creating from Raw Value
Enums with raw values get a failable initializer:
let maybeLevel = Level(rawValue: 2) // .intermediate (optional)
let invalid = Level(rawValue: 5) // nilExample check:
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:
enum MathOp {
case value(Int)
indirect case add(MathOp, MathOp)
indirect case subtract(MathOp, MathOp)
}Or indirect for the whole enum:
indirect enum MathOp {
case value(Int)
case add(MathOp, MathOp)
case subtract(MathOp, MathOp)
}Example: 10 + (5 - 3)
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:
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