Enumerations
Enumerations (enum) define a finite set of related values, supporting raw values, associated values, and recursive definitions. They are powerful for modeling state and data variants.
Basic Enumerations
Define with enum, where cases represent possible values.
Example:
enum Direction {
case north
case south
case east
case west
}
let heading = Direction.north
switch heading {
case .north: print("Going north")
default: print("Other direction")
}Raw Values
Assign raw values (e.g., Int, String) to cases, automatically incrementing for integers.
Example: String Raw Values:
enum Planet: String {
case mercury = "Mercury"
case venus = "Venus"
case earth = "Earth"
}
let planet = Planet(rawValue: "Earth") // .earth
print(planet?.rawValue ?? "Unknown") // "Earth"Example: Integer Raw Values:
enum StatusCode: Int {
case ok = 200
case notFound = 404
case serverError = 500
}
let code = StatusCode(rawValue: 404) // .notFound
print(code?.rawValue) // 404Associated Values
Store additional data with cases, enabling flexible data modeling.
Example:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
let barcode = Barcode.upc(123, 456, 789, 0)
switch barcode {
case let .upc(a, b, c, d):
print("UPC: \(a)-\(b)-\(c)-\(d)")
case let .qrCode(content):
print("QR: \(content)")
}Recursive Enumerations
Use indirect for cases that reference the enum itself.
Example:
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
func evaluate() -> Int {
switch self {
case let .number(value):
return value
case let .addition(left, right):
return left.evaluate() + right.evaluate()
case let .multiplication(left, right):
return left.evaluate() * right.evaluate()
}
}
}
let expr = ArithmeticExpression.addition(.number(5), .multiplication(.number(2), .number(3)))
print(expr.evaluate()) // 5 + (2 * 3) = 11Methods and Properties
Enums can have methods, computed properties, and static members.
Example:
enum TrafficLight {
case red, yellow, green
var duration: Int {
switch self {
case .red: return 30
case .yellow: return 5
case .green: return 20
}
}
func next() -> TrafficLight {
switch self {
case .red: return .green
case .green: return .yellow
case .yellow: return .red
}
}
static func defaultLight() -> TrafficLight {
return .red
}
}
var light = TrafficLight.red
print(light.duration) // 30
light = light.next() // .green
print(TrafficLight.defaultLight()) // .redCaseIterable Protocol
Generate a collection of all cases with CaseIterable.
Example:
enum Day: CaseIterable {
case monday, tuesday, wednesday, thursday, friday, saturday, sunday
}
print(Day.allCases.count) // 7
for day in Day.allCases {
print(day)
}Frozen Enums
Use @frozen (in libraries) to guarantee no new cases are added, improving performance.
Example:
@frozen enum LogLevel {
case debug
case info
case error
}Enums with Protocols
Enums can conform to protocols.
Example:
protocol Describable {
var description: String { get }
}
enum FileType: String, Describable {
case pdf, doc, txt
var description: String {
return "File type: \(rawValue.uppercased())"
}
}
let file = FileType.pdf
print(file.description) // "File type: PDF"Best Practices
- Use Enums for States: Model finite states (e.g., UI modes, API responses).
- Associated Values: Store variant-specific data.
- Raw Values: Use for serialization or external representation.
- CaseIterable: Iterate over cases when needed.
- Methods: Add logic to enums for self-contained behavior.
- Frozen Enums: Use in libraries for performance and stability.
- Exhaustive Switches: Avoid
defaultfor enums to catch future changes.
Troubleshooting
- Non-Exhaustive Switch: Add all cases or
default. - Raw Value Failure: Verify input matches raw value type.
- Recursive Enum Crash: Ensure
indirectis used correctly. - Protocol Conformance: Check required members are implemented.
- Performance: Avoid complex logic in enum methods for critical paths.
Example: Comprehensive Enum Usage
enum NetworkRequest: CaseIterable, Describable {
case get(url: String)
case post(url: String, data: [String: Any])
case delete(id: Int)
var description: String {
switch self {
case let .get(url):
return "GET request to \(url)"
case let .post(url, data):
return "POST request to \(url) with \(data)"
case let .delete(id):
return "DELETE request for ID \(id)"
}
}
var method: String {
switch self {
case .get: return "GET"
case .post: return "POST"
case .delete: return "DELETE"
}
}
static var allMethods: [String] {
allCases.map { $0.method }
}
}
let requests: [NetworkRequest] = [
.get(url: "https://api.example.com/users"),
.post(url: "https://api.example.com/users", data: ["name": "Alice"]),
.delete(id: 123)
]
for request in requests {
print(request.description)
}
print(NetworkRequest.allMethods) // ["GET", "POST", "DELETE"]