Enumerations
|
This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline. This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
An enumeration defines a common type for a group of related values and lets you work with those values in a
type-safe way throughout your code. Unlike enumerations in C, a Swift enum case is not backed by an integer by
default — each case is a first-class value in its own right, and cases may carry their own associated data.
Enum Syntax and Matching with switch
enum CompassPoint {
case north
case south
case east
case west
}
var directionToHead = CompassPoint.west
directionToHead = .west // the type is already known, so `.west` alone is enough
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
} // exhaustive: every CompassPoint case is handled, no default needed
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune // multiple cases on one line
}
A switch over an enum must be exhaustive: every case declared on the type has to be handled, either directly or
via default, which is what lets the compiler catch a forgotten case the moment a new one is added (see
Pattern Matching for the enumeration-case pattern this
relies on, and Control Flow for switch as a statement and
an expression).
CaseIterable
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available") // 3
for beverage in Beverage.allCases {
print(beverage)
}
Conforming an enum to CaseIterable synthesizes an allCases static property listing every case in declaration
order, useful for populating a menu, iterating exhaustively in a test, or counting the cases without maintaining a
separate list by hand.
Associated Values
enum Barcode {
case upc(Int, Int, Int, Int) // unlabeled associated values
case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case .qrCode(let productCode):
print("QR code: \(productCode)")
}
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check): // a single `let` before the pattern binds every element
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check)")
case let .qrCode(productCode):
print("QR code: \(productCode)")
}
enum HTTPResponse {
case ok(body: String) // labeled associated values document what each payload means
case redirect(location: String, statusCode: Int)
case error(message: String)
}
let response = HTTPResponse.redirect(location: "/login", statusCode: 302)
if case .redirect(let location, let statusCode) = response {
print("redirecting to \(location) with status \(statusCode)")
}
Associated values let each case carry data of its own choosing, extracted through the same pattern-matching
machinery as any other enumeration-case pattern — a switch, if case, or guard case. Labels on associated
values (body:, location:, statusCode:) are purely documentation at the declaration site; extracting them
still binds by position, matching the label names used when constructing the case.
Raw Values
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
enum Planet2: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune // implicit assignment: 1, 2, 3...
}
print(Planet2.mars.rawValue) // 4
enum CompassPoint2: String {
case north, south, east, west // implicit assignment: the case name itself, as a String
}
print(CompassPoint2.south.rawValue) // "south"
let possiblePlanet = Planet2(rawValue: 7) // init?(rawValue:) -- always failable
if let possiblePlanet {
print(possiblePlanet)
} else {
print("no such planet")
}
let notAPlanet = Planet2(rawValue: 11) // nil: no case has raw value 11
A raw-value enum stores one pre-populated value per case, of a single fixed type (Character, String, or any
integer or floating-point type). Integer raw values auto-increment from the first explicit one (or from 0);
string raw values default to the case’s own name when omitted. The compiler-synthesized init?(rawValue:) is
always failable, since not every raw value maps to a case — unlike associated values, which are supplied at
construction and read back by pattern matching rather than through a raw-value initializer.
Recursive Enums with indirect
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
// enum ArithmeticExpression2 {
// case number(Int)
// indirect case addition(ArithmeticExpression2, ArithmeticExpression2) // `indirect` on just this case also works
// case multiplication(ArithmeticExpression2, ArithmeticExpression2)
// }
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case .number(let value):
value
case .addition(let left, let right):
evaluate(left) + evaluate(right)
case .multiplication(let left, let right):
evaluate(left) * evaluate(right)
}
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
print(evaluate(product)) // 18
An enum case whose associated value is the enum’s own type would otherwise have unbounded size at compile time;
marking the case (or the whole enum) indirect tells the compiler to box that case’s storage on the heap so the
recursion has a fixed, indirect size, at the cost of an extra allocation and pointer indirection for that case.
Methods, Computed Properties and Initializers on Enums
enum TrafficLight {
case red, yellow, green
var next: TrafficLight { // computed property
switch self {
case .red: .green
case .green: .yellow
case .yellow: .red
}
}
func description() -> String { // instance method
switch self {
case .red: "stop"
case .yellow: "caution"
case .green: "go"
}
}
init(fromSeconds seconds: Int) { // custom initializer
self = seconds.isMultiple(of: 2) ? .red : .green
}
}
var light = TrafficLight.red
light = light.next // .green
print(light.description())
Like structs and classes, an enum can declare instance methods, computed properties, and its own initializers — covered in full in Properties and Methods and Subscripts. It cannot, however, declare stored properties per case beyond the case’s own associated values.
Enums as Namespaces
enum MathConstants { // never instantiated -- purely a namespace
static let pi = 3.14159
static func square(_ x: Double) -> Double { x * x }
}
print(MathConstants.pi)
print(MathConstants.square(4))
An enum with no cases at all can never be instantiated, which makes it a convenient namespace for grouping related
static constants and functions without accidentally creating a value of the type — a lighter-weight alternative
to a struct used the same way.
OptionSet Structs vs. Enums
struct ShippingOptions: OptionSet {
let rawValue: Int
static let nextDay = ShippingOptions(rawValue: 1 << 0)
static let secondDay = ShippingOptions(rawValue: 1 << 1)
static let priority = ShippingOptions(rawValue: 1 << 2)
static let standard = ShippingOptions(rawValue: 1 << 3)
static let express: ShippingOptions = [.nextDay, .secondDay]
}
let options: ShippingOptions = [.priority, .standard]
if options.contains(.standard) {
print("standard shipping included")
}
OptionSet — always a struct, never an enum — models a combinable set of flags (any subset of options at
once, via [.a, .b]), which an enum’s mutually-exclusive cases cannot express directly; reach for an enum when
exactly one case applies at a time, and for OptionSet when several may apply together.
Comparable Synthesis
enum SkillLevel: Comparable {
case beginner
case intermediate
case expert
}
let levels: [SkillLevel] = [.expert, .beginner, .intermediate]
print(levels.sorted()) // [.beginner, .intermediate, .expert]
print(SkillLevel.beginner < SkillLevel.expert) // true
Conforming a raw-value-less enum to Comparable synthesizes < from declaration order, so cases compare in the
order they were written — convenient for a small, ordered set of levels or states without hand-writing the
comparison.
Enums as State Machines
enum DownloadState {
case idle
case loading
case loaded(data: Data)
case failed(error: Error)
mutating func start() {
guard case .idle = self else { return }
self = .loading
}
mutating func succeed(data: Data) {
guard case .loading = self else { return }
self = .loaded(data: data)
}
mutating func fail(error: Error) {
guard case .loading = self else { return }
self = .failed(error: error)
}
}
An enum with one case per state, each carrying exactly the data that state needs (a loaded case’s data, a
failed case’s error), models a state machine directly: the compiler enforces that every transition method
starts from a known set of cases, and a switch over the state can never omit a case it hasn’t been taught about.
mutating methods that reassign self — as start(), succeed(data:) and fail(error:) do above — are
covered in full in Methods and Subscripts.
See Also
-
Pattern Matching — the enumeration-case pattern used throughout this page’s
switch/if caseexamples. -
Structures and Classes — enums as one of Swift’s two value-type kinds, alongside structs.
-
Properties — computed properties and property observers in full, applicable to enum cases too.
-
Methods and Subscripts —
mutatingmethods on value types, the mechanism behind this page’s state-machine example.