Control flow

Swift conditionals: the switch statement

Learn the switch statement in Swift: exhaustive cases, no implicit fallthrough, matching ranges, tuples, value binding with case let, and where clauses.

8 minute lesson

~~~

This tutorial belongs to the Swift series

A switch statement compares a value against multiple possible cases and runs the code of the first case that matches. It’s the cleaner alternative to a long chain of if/else if blocks:

let name = "Roger"

switch name {
case "Roger":
    print("Hello, mr. Roger!")
default:
    print("Hello, \(name)")
}

Every case must be covered

Swift requires a switch to be exhaustive: every possible value of the thing you’re switching on must be handled. A string can hold anything, so here the default case is mandatory. Remove it and the code doesn’t compile.

With an enumeration you can list all the cases instead, and skip default entirely:

enum Animal {
    case dog
    case cat
}

let animal = Animal.dog

switch animal {
case .dog:
    print("Hello, dog!")
case .cat:
    print("Hello, cat!")
}

This is where exhaustiveness earns its keep. Say you later add case rabbit to the enum. Every switch that handles Animal without a default stops compiling, and the compiler points you at each place that needs to handle rabbits. In most languages you’d discover the missing case at runtime, or never.

My advice: when switching on an enum, list the cases explicitly instead of adding a default. You give up a little convenience now for a compiler safety net later.

No implicit fallthrough

In C and languages inspired by it, forgetting a break makes execution fall into the next case, a classic source of bugs. Swift does the opposite: it runs the matched case and exits the switch. No break needed.

If you genuinely want fallthrough behavior, you ask for it with the fallthrough keyword:

let number = 5

switch number {
case 5:
    print("It's five")
    fallthrough
case 10:
    print("It's five or ten")
default:
    break
}
// prints both lines

Note that fallthrough does not check the next case’s condition, it just executes its body. You’ll rarely need it.

That default: break is also worth noticing: a case body can’t be empty in Swift, so break is how you say “do nothing here”.

Matching ranges

A case can match a range of values:

let age = 20

switch age {
case 0..<18:
    print("You can't drive")
case 18..<70:
    print("You can drive")
default:
    print("Better check with your doctor first")
}

Matching tuples

You can switch on a tuple and match its parts individually. The underscore matches any value:

let coordinates = (2, 0)

switch coordinates {
case (0, 0):
    print("At the origin")
case (_, 0):
    print("On the x axis")
case (0, _):
    print("On the y axis")
default:
    print("Somewhere else")
}

Value binding and where

With case let you capture the matched value in a constant, and with where you add a condition to the case:

let temperature = 34

switch temperature {
case let t where t < 12:
    print("\(t) degrees, bring a jacket")
case let t where t > 30:
    print("\(t) degrees, stay hydrated")
default:
    print("Nice weather")
}

The first case whose where condition is true wins, and t holds the value inside that case’s body.

Once you combine ranges, tuples, binding, and where, switch becomes a small pattern matching engine, far more capable than the switch you may know from other languages.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →