Many values in an app come from a fixed list of options: a compass direction, a T-shirt size, the state of a download, a payment method. Storing them as strings like "large" invites typos and gives the compiler nothing to check. An enum (enumeration) defines a type whose only possible values are the cases you list.
Swift's enums are unusually powerful. Cases can have raw values, such as a string for saving to a file. They can carry associated values: different data for each case, like a download that's in progress with a percentage, or failed with an error message. Enums can also have properties and methods, just like structs.
Combined with switch, enums make code both safer and clearer: the compiler checks that every case is handled, and adding a new case later points you to every place that needs updating. In this lesson you'll use all of these features with realistic examples.
Declaring and using enums
enum Direction {
case north, south, east, west
}
var heading = Direction.north
heading = .west // the type is known, so .west is enough
switch heading {
case .north: print("Up")
case .south: print("Down")
case .east, .west: print("Sideways")
}- Enum names use UpperCamelCase; cases use lowerCamelCase.
- Once Swift knows the type, write cases with a leading dot:
.west. - A switch that covers every case needs no default, and the compiler warns if a case is missing.
Raw values and CaseIterable
Give every case a fixed raw value of type String, Int or another simple type:
enum Size: String, CaseIterable {
case small = "S", medium = "M", large = "L"
}
Size.large.rawValue // "L"
Size(rawValue: "M") // Optional(Size.medium)
Size.allCases.count // 3- For String raw values you can leave the value out, and it defaults to the case name:
case smallhas the raw value"small". - Int raw values count up from 0 automatically.
init(rawValue:)returns an optional, because the text might not match any case.- Adopting
CaseIterableprovidesallCases, a list of every case, which is handy for menus and pickers.
Associated values
Each case can carry its own data, of different types:
enum DownloadState {
case waiting
case inProgress(percent: Int)
case finished(fileURL: String)
case failed(message: String)
}
let state = DownloadState.inProgress(percent: 40)
switch state {
case .waiting: print("Queued")
case .inProgress(let percent): print("\(percent)%")
case .finished(let url): print("Saved to \(url)")
case .failed(let message): print("Error: \(message)")
}let inside the case pattern extracts the data. Associated values are the natural way to model states and results, where each outcome needs different information.
Properties and methods
Enums can have computed properties and methods, often implemented with switch self:
enum Planet {
case mercury, earth, mars
var gravity: Double {
switch self {
case .mercury: 3.7
case .earth: 9.8
case .mars: 3.7
}
}
}A method that changes self to another case must be mutating. In fact, Swift's own Optional is an enum with two cases, .some(value) and .none, which is why switch and if case work on optionals too.
Comparing enum values
You can compare simple enum cases with == straight away, because Swift makes enums without associated values Equatable automatically:
if heading == .north { print("Heading up") }Once cases carry associated values, Swift can't assume how to compare them, so == stops working until you ask for it. Add Equatable to the declaration and Swift writes the comparison for you, as long as every associated value is Equatable too:
enum Status: Equatable {
case loading
case failed(String)
}
print(Status.failed("Offline") == .failed("Offline")) // trueTo check only which case a value is, ignoring its data, use pattern matching instead: if case .failed = status { ... } works with or without Equatable.
One enum instead of several Booleans
A common beginner design tracks a screen's state with separate flags:
var isLoading = false
var hasError = false
var items: [String] = []Nothing stops isLoading and hasError both being true, a combination that makes no sense. An enum makes impossible states impossible to write:
enum LoadState {
case idle
case loading
case loaded([String])
case failed(String)
}
var state = LoadState.idleThe value is always exactly one case, and the data travels with the case that needs it: items only exist once loading has succeeded, and an error message only exists after a failure. A switch over state then forces every screen to handle each situation.
Swift Enums Syntax
Enum syntax
enum Light { case red, amber, green }
enum Size: String, CaseIterable { case small = "S", large = "L" }
Size.small.rawValue Size(rawValue: "L") Size.allCases
enum Result { case success(Int), failure(String) }
switch r {
case .success(let n): print(n)
case .failure(let msg): print(msg)
}
if case .success(let n) = r { print(n) }Swift Enums Summary
- An enum defines a type with a fixed set of cases; write
.casewhen the type is known. - A switch over every case needs no default and is checked by the compiler.
- Raw values (String, Int) map cases to fixed values; init(rawValue:) returns an optional.
- CaseIterable adds allCases; associated values let each case carry its own data.
- Enums can have computed properties and methods; Optional itself is an enum.
Swift Enums Examples
Each example shows the code, the output it produces and what each part does.
Sizes with raw values
enum Size: String, CaseIterable {
case small = "S"
case medium = "M"
case large = "L"
var price: Double {
switch self {
case .small: 2.5
case .medium: 3.0
case .large: 3.5
}
}
}
for size in Size.allCases {
print("\(size.rawValue): £\(size.price)")
}
let typed = "M"
if let chosen = Size(rawValue: typed) {
print("You chose \(chosen)")
}
print(Size(rawValue: "XL") == nil)S: £2.5
M: £3.0
L: £3.5
You chose medium
trueLine-by-line explanation
- Line 1String raw values and CaseIterable together.
- Lines 6 to 12A computed property using switch self; each branch is a single value.
- Line 15allCases lists every size, in declaration order.
- Line 20init(rawValue:) returns an optional, so if let unwraps it. Printing an enum shows its case name.
Associated values for download states
enum DownloadState {
case waiting
case inProgress(percent: Int)
case finished(fileName: String)
case failed(message: String, canRetry: Bool)
}
func label(for state: DownloadState) -> String {
switch state {
case .waiting:
return "Waiting..."
case .inProgress(let percent):
return "Downloading \(percent)%"
case .finished(let name):
return "Saved \(name)"
case .failed(let message, let canRetry):
return message + (canRetry ? " (tap to retry)" : "")
}
}
let states: [DownloadState] = [.waiting, .inProgress(percent: 45),
.finished(fileName: "photo.jpg"),
.failed(message: "No connection", canRetry: true)]
states.forEach { print(label(for: $0)) }Waiting...
Downloading 45%
Saved photo.jpg
No connection (tap to retry)Line-by-line explanation
- Lines 3 to 5Each case carries different data.
- Line 9Every case is handled, so no default is needed.
- Lines 12 to 17let extracts the associated values inside each case.
A mutating enum and if case
enum TrafficLight {
case red, green, amber
mutating func next() {
switch self {
case .red: self = .green
case .green: self = .amber
case .amber: self = .red
}
}
}
var light = TrafficLight.red
for _ in 1...4 {
print(light, terminator: " ")
light.next()
}
print()
let result: Result<Int, Error> = .success(42)
if case .success(let value) = result {
print("Got \(value)")
}red green amber red
Got 42Line-by-line explanation
- Lines 4 to 10A mutating method replaces self with the next case.
- Lines 14 to 17The light cycles through its states.
- Line 20Swift's built-in Result is an enum; if case checks for one case without a full switch.
Common Swift Enums Mistakes
Strings instead of an enum
var size = "meduim" // typo compiles finevar size = Size.medium // a typo is a compile errorForcing a raw value conversion
let size = Size(rawValue: input)! // crashes for unexpected inputlet size = Size(rawValue: input) ?? .mediumdefault hiding new cases
switch state {
case .waiting: showSpinner()
default: break // a new case added later is silently ignored
}switch state {
case .waiting: showSpinner()
case .inProgress, .finished, .failed: break
}Swift Enums in Real Projects
Enums in Swift apps
Apps model screen state with enums such as case loading, loaded([Item]), failed(Error), and SwiftUI views switch over them to show the right content. Raw-value enums define settings, sort orders and tabs, often saved with @AppStorage. Swift's own Optional and Result are enums, and network code commonly returns Result<Data, Error>.
Swift Enums FAQ
How do I convert a string to an enum in Swift?
Give the enum a String raw type, then use init(rawValue:): Size(rawValue: "M"). It returns an optional, which is nil when no case has that raw value, so unwrap it with if let or provide a default with ??.
How do I loop over all cases of an enum in Swift?
Add CaseIterable to the enum's declaration, then use the generated allCases array: for size in Size.allCases { print(size) }. It lists the cases in the order they're declared.
What is the difference between raw values and associated values?
A raw value is a fixed value chosen when you declare the case, the same every time, such as case small = "S". An associated value is data attached when you create a value, and it can differ each time, such as .inProgress(percent: 40). An enum with a raw type can't have associated values at all.
Can a Swift enum have methods and properties?
Yes. Enums can have methods, computed properties, initializers and static members, and they can conform to protocols. They can't have stored instance properties: put data in associated values instead.
Why do Swift enum cases start with a dot?
The dot is shorthand for the enum's name when Swift already knows the type. heading = .west means heading = Direction.west, because heading is a Direction. When the type isn't known, you write the full name.
Swift Enums Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the declaration so the enum gets an allCases list.
enum Weekday: {
case mon, tue, wed, thu, fri
}
What does this print?
enum Coin: Int { case penny = 1, nickel = 5, dime = 10 }
print(Coin.dime.rawValue)
What type does Size(rawValue: "XL") return?
What does this print?
enum Shape { case circle(r: Double), square(side: Double) }
let s = Shape.square(side: 3)
switch s {
case .circle(let r): print(3.14 * r * r)
case .square(let side): print(side * side)
}
Why avoid default when switching over your own enum?
Swift Enums Quiz
5 questions to check what you learned.
How do you write a case when Swift already knows the enum type?
- .west
- Direction::west
- west()
- "west"
What are associated values?
- Raw values
- Static properties
- Data attached to a specific case, which can differ for each case
- Default values
What's the default raw value of
case smallin an enum with String raw values?- ""
- "small"
- 0
- nil
Which built-in Swift type is an enum?
- String
- Array
- Int
- Optional
What must a method that changes
selfin an enum be marked?- static
- mutating
- override
- final