Swift Enums and Optionals¶
Enums define a finite set of named cases. Optionals represent values that may or may not exist. Together with switch statements, they form Swift's powerful pattern matching system used extensively in SwiftUI for state management and data handling.
Key Facts¶
- Enums can have raw values (String, Int, Double) and computed properties
CaseIterableconformance provides.allCasesarray for iteration- Optionals are declared with
?suffix:var death: Death? nilrepresents the absence of a value- Swift's switch must be exhaustive - cover all cases or use
default guard letis preferred for early exits;if letfor conditional blocks- Force unwrap
!should only be used when nil is a programmer error
Patterns¶
Enum with Raw Values¶
enum Currency: Double, CaseIterable {
case copperPenny = 0.01
case silverPenny = 0.1
case silverSickle = 1.0
case goldSickle = 10.0
case goldGalleon = 100.0
}
// Access raw value
Currency.goldGalleon.rawValue // 100.0
// Iterate all cases
for currency in Currency.allCases {
print(currency)
}
Computed Properties on Enums¶
enum APType: String, Decodable, CaseIterable {
case land
case air
case sea
}
enum Currency: Double {
case copperPenny = 0.01
// ...
var name: String {
switch self {
case .copperPenny: return "Copper Penny"
case .silverPenny: return "Silver Penny"
// ... etc
}
}
var image: Image {
switch self {
case .copperPenny: return Image(.copperPenny)
// ... etc
}
}
}
Methods on Enums via Extension¶
extension Currency {
func convert(_ amountString: String, to currency: Currency) -> String {
guard let doubleAmount = Double(amountString) else { return "" }
let convertedAmount = (doubleAmount / self.rawValue) * currency.rawValue
return String(convertedAmount)
}
}
// Usage
leftCurrency.convert(leftAmount, to: rightCurrency)
Enum for State Tracking¶
enum FetchStatus {
case notStarted
case fetching
case success
case failed(error: Error) // associated value
}
enum BookStatus {
case active
case inactive
case locked
}
Switch Statement¶
switch vm.status {
case .notStarted:
EmptyView()
case .fetching:
ProgressView()
case .success:
Text("Data loaded")
case .failed(let error): // extract associated value
Text(error.localizedDescription)
}
Switch must be exhaustive - cover all cases or use default.
Optionals¶
var death: Death? = nil // optional Death - no value yet
// 1. if let (safe unwrap)
if let death = character.death {
Text(death.cause) // only runs if death is not nil
}
// 2. guard let (early exit)
guard let url = URL(string: urlString) else { return nil }
// 3. nil coalescing
let name = character.death?.responsible ?? "Unknown"
// 4. Optional chaining
let responsible = character.death?.responsible // nil if death is nil
// 5. Force unwrap (!) - only when certain non-nil
URL(string: "https://apple.com")!
Optionals in Codable Models¶
struct Character: Decodable {
let name: String
var death: Death? // optional - nil if key absent in JSON
}
JSONDecoder leaves optional properties as nil when the key is missing from JSON data.
Gotchas¶
- Force unwrapping (
!) a nil value crashes the app at runtime - use only when the value is guaranteed guard letmust exit the enclosing scope (return, break, continue, throw)- Enum raw values must be unique and of the same type
ForEachin SwiftUI only accepts half-open ranges (0..<5), not closed ranges (0...4)- Optional comparisons:
nil == nilistrue, butnil < 0does not compile without explicit typing - When using enums with
Picker, wrap the optional type:Text("All").tag(APType?.none)for the nil case
See Also¶
- [[swift-fundamentals]] - variables, types, control flow
- [[swift-structs-and-classes]] - protocols, computed properties
- [[swiftui-networking]] - enums for fetch status tracking
- [[swiftui-state-and-data-flow]] - using enums with @State