EthicsComputerCourses
Join training +91 90532 10052

Enums & switch

Swift · Lesson 13 of 30 Beginner 6 min read Updated

Many values in real life come from a fixed list of choices: a traffic light is red, yellow or green; a payment is cash, UPI or card; an order is placed, packed, shipped or delivered. Storing these as strings like "shiped" invites typing mistakes that the compiler cannot catch.

Swift's enumerations (enums) define such a list as a type of its own. Combined with switch, which the compiler checks for completeness, enums are one of Swift's most loved features. In this lesson you will define enums, give them raw values and attached data, and match them with switch patterns.

Defining an enum and switching over it

An enum lists its cases. Once Swift knows a variable's type, you can write just .green instead of TrafficLight.green.

Swift
enum TrafficLight {
    case red, yellow, green
}

var light = TrafficLight.red
light = .green

switch light {
case .red:
    print("Stop")
case .yellow:
    print("Get ready")
case .green:
    print("Go")
}
print(light)
Output
Go
green

The switch has a case for every value, so it needs no default. This is the big benefit: if you later add case flashing to the enum, the compiler shows an error at every switch that does not handle it yet. A default would hide that, so avoid default when switching over your own enums.

Raw values and CaseIterable

An enum can have a raw value of type Int, String or another simple type. Integer raw values count up automatically; string raw values default to the case name. Add CaseIterable to get an allCases array.

Swift
enum Weekday: Int, CaseIterable {
    case monday = 1, tuesday, wednesday, thursday, friday, saturday, sunday
}
print(Weekday.wednesday.rawValue)
print(Weekday(rawValue: 6) ?? .monday)
print(Weekday(rawValue: 9) == nil)
print(Weekday.allCases.count)

enum Meal: String, CaseIterable {
    case breakfast, lunch
    case dinner = "Dinner (after 7 pm)"
}
for meal in Meal.allCases {
    print(meal.rawValue)
}
Output
3
saturday
true
7
breakfast
lunch
Dinner (after 7 pm)

Creating an enum from a raw value with Weekday(rawValue:) returns an optional, because a number like 9 matches no case. Raw values are handy when saving to a file or reading JSON, and allCases is perfect for filling a picker in SwiftUI.

Associated values

Cases can carry extra data called associated values, and each case can carry different data. A switch pulls the data out with let.

Swift
enum Payment {
    case cash
    case upi(id: String)
    case card(lastFour: String, emi: Int)
}

func describe(_ payment: Payment) -> String {
    switch payment {
    case .cash:
        return "Paid in cash"
    case .upi(let id):
        return "UPI from \(id)"
    case .card(let lastFour, let emi) where emi > 0:
        return "Card ending \(lastFour), \(emi) month EMI"
    case .card(let lastFour, _):
        return "Card ending \(lastFour)"
    }
}

let payments: [Payment] = [.cash, .upi(id: "kabir@upi"),
                           .card(lastFour: "4821", emi: 6), .card(lastFour: "9910", emi: 0)]
for payment in payments {
    print(describe(payment))
}
print(Payment.upi(id: "isha@upi"))
Output
Paid in cash
UPI from kabir@upi
Card ending 4821, 6 month EMI
Card ending 9910
upi(id: "isha@upi")

A where clause adds an extra condition to a case, and _ ignores a value you do not need. Cases are checked from top to bottom, so the EMI case must come before the general card case. Associated values let one type describe very different situations safely: a UPI payment simply cannot have a missing ID.

Powerful switch patterns

switch works with any type, not only enums. Cases can match several values, ranges, tuples and conditions, and can bind parts of the value to names.

Swift
func classify(score: Int, wickets: Int) -> String {
    switch (score, wickets) {
    case (100..., _):
        return "Century!"
    case (50..<100, 0...2):
        return "Strong start"
    case (_, 8...10):
        return "Collapse"
    case let (runs, out) where runs > out * 20:
        return "Steady"
    default:
        return "Struggling"
    }
}

print(classify(score: 120, wickets: 9))
print(classify(score: 64, wickets: 1))
print(classify(score: 40, wickets: 9))
print(classify(score: 90, wickets: 3))
print(classify(score: 30, wickets: 4))
Output
Century!
Strong start
Collapse
Steady
Struggling

Trace the fourth call: 90 is in 50..<100 but 3 wickets is not in 0...2, and 3 is not in 8...10, so Swift reaches the where case: 90 is more than 3 times 20, so the result is "Steady". Integers have endless possible values, so this switch needs a default. You can also list several values in one case, for example case "A", "B":.

Methods, computed properties and if case

Enums can have computed properties and methods, just like structs. A method that changes self must be marked mutating. To test for just one case without a full switch, use if case.

Swift
enum OrderStatus {
    case placed, packed, shipped(trackingID: String), delivered

    var isActive: Bool {
        switch self {
        case .delivered: false
        default: true
        }
    }

    mutating func advance() {
        switch self {
        case .placed: self = .packed
        case .packed: self = .shipped(trackingID: "IN2026X")
        case .shipped: self = .delivered
        case .delivered: break
        }
    }
}

var status = OrderStatus.placed
for _ in 1...4 {
    status.advance()
    print(status, status.isActive)
}
if case .delivered = status {
    print("Ask for a review")
}
Output
packed true
shipped(trackingID: "IN2026X") true
delivered false
delivered false
Ask for a review

The fourth advance() does nothing because a delivered order stays delivered; break in a switch case means "do nothing here". isActive uses a switch expression with a default, which is fine for a property that only cares about one case.

Common mistakes

  • Using strings for fixed choices. "shipped" versus "Shipped" bugs disappear with an enum.
  • Adding default to your own enum switches. You lose the compiler's reminder when new cases are added.
  • Forgetting that init(rawValue:) is optional. Unwrap it or use ??.
  • Putting a general case before a specific one. The first matching case wins; order cases from most to least specific.

Key points

  • An enum defines a type with a fixed set of cases; .caseName works when the type is known.
  • switch must be exhaustive and never falls through; avoid default for your own enums.
  • Raw values (Int, String) and CaseIterable connect enums to stored data and lists.
  • Associated values attach different data to each case; case .upi(let id) extracts it.
  • switch patterns include ranges, tuples, let bindings, _ and where clauses.
  • Enums can have computed properties and mutating methods; if case tests a single case.

Exercise

Create an enum Ticket for a cinema in Pune with cases regular, premium and recliner(withFood: Bool). Add a computed property price returning 180, 320, and 550 for the recliner (or 750 with food). Print the price of each ticket in the array [.regular, .recliner(withFood: true), .premium, .recliner(withFood: false)] and the total.

Show solution
Swift
enum Ticket {
    case regular, premium
    case recliner(withFood: Bool)

    var price: Int {
        switch self {
        case .regular: 180
        case .premium: 320
        case .recliner(let withFood): withFood ? 750 : 550
        }
    }
}

let booking: [Ticket] = [.regular, .recliner(withFood: true), .premium, .recliner(withFood: false)]
var total = 0
for ticket in booking {
    print(ticket.price)
    total += ticket.price
}
print("Total: \(total)")
Output
180
750
320
550
Total: 1800

The switch covers every case, so it needs no default. The recliner case extracts withFood and uses a ternary to choose between the two prices.

Quick Quiz

1. Why should you avoid default when switching over your own enum?

2. What does Weekday(rawValue: 9) return if no case has raw value 9?

3. How do you read the ID from case upi(id: String) in a switch?

4. Which keyword must a method that changes self in an enum have?

Learn Swift with a mentor

Free lessons get you started. Our Mobile App Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call