The switch statement in Swift lets you inspect a value and match it with a number of cases. It’s particularly effective for taking concise decisions based on one variable that can contain a number of possible values. Using the switch statement often results in more concise code that’s easier to read.
In this app development tutorial, you’ll learn how to use the switch statement in Swift. We’ll get into:
Get Started
Here’s what the Swift Language Guide has to say about the switch statement:
A switch statement considers a value and compares it against several possible matching patterns.
Let’s find out what that means, with an example. Consider the following enumeration:
enum Compass {
case north
case east
case south
case west
}
When you want to find your bearings, you could use this code:
let heading = Compass.south
if heading == .north {
print(“You’re heading north!”)
} else if heading == .east {
print(“You’re heading east!”)
} else if heading == .south {
print(“You’re heading south!”)
} else if heading == .west {
print(“You’re heading west!”)
}
The above code uses an if statement to evaluate the value of heading, and print out the relevant line of text.
Here’s how you can do the same using a switch statement:
switch heading {
case .north:
print(“You’re heading north!”)
case .east:
print(“You’re heading east!”)
case .south:
print(“You’re heading south!”)
case .west:
print(“You’re heading west!”)
}
The syntax of the switch statement is simple:
The switch statement is much more powerful than it seems, especially compared to the if statement. One of its advantages is that every switch block needs to be exhaustive.
Here, check out what happens when we forget to include .east:
switch heading {
case .north:
print(“You’re heading north!”)
case .south:
print(“You’re heading south!”)
case .west:
print(“You’re heading west!”)
}
The output is: error: switch must be exhaustive.
Oops! That means that we’ll need to assess every value of Compass. When we don’t, we’ll see a compile-time error. This is particularly helpful if you’re changing an enumeration later on in your app. You wouldn’t get a warning if you had used if statements.
The advantages of the switch statement go beyond merely checking enums for exhaustiveness (Scrabble word!). Its cases are checked at compile-time, so you can spot numerous errors before your app runs.
Even though a switch statement needs to be exhaustive, you don’t have to explicitly specify every option.
Here, consider the following Authorization enumeration:
enum Authorization {
case granted
case undetermined
case unauthorized
case denied
case restricted
}
When you want to give a user access to a resource based on their authorization state, you could do this:
switch state {
case .granted:
print(“Access granted. You may proceed.”)
default:
print(“YOU SHALL NOT PASS!!”)
}
The above code uses the default case to respond to any case that’s not handled. This makes the switch exhausive.
And you can also use compound cases, like this:
switch state {
case .granted:
print(“Access granted. You may proceed.”)
case .undetermined:
print(“Please provide your clearance code.”)
case .unauthorized, .denied, .restricted:
print(“Access denied!”)
}
The last case combines several cases on one line. Everyone of those .unauthorized, .denied and .restricted cases will trigger the “Access denied!” response.
And last but not least, let’s take a look at implicit fallthrough. In most programming languages, switch statement cases implicitly “fall through” to the next case. Execution starts with the first matching case, and continues down until you explicitly stop execution with break.
In Swift, it’s exactly the opposite. Every switch case will automatically halt. You don’t need to use break, because Swift switch statements don’t implicitly fall through.
This is a deliberate design decision. In most cases, you don’t want your case to fall through. When you do, you can use fallthrough explicitly. Like this:
var message = “Response: ”
let state = Authorization.undetermined
switch state {
case .granted:
message += “Access granted. You may proceed.”
case .undetermined:
message += “Please provide your clearance code. ”
fallthrough
default:
message += “Access denied!”
}
print(message)
In the above example, the .undetermined case falls through to the default case. So, when state is .undetermined, both cases are executed, and two strings are added to message, which results in this output:
Output: Response: Please provide your clearance code. Access denied!
And that’s because of the fallthrough keyword in the .undetermined case block.
You don’t have to explicitly break a case block, but if you want to prematurely exit a particular case you can use break. Just as with for and while loops.
You can use the switch statements to match intervals and ranges. Consider, for instance, how humans perceive visible light with different wavelengths:
We can check if a particular wavelength produces the color violet or orange. Like this:
let wavelength = 398
if wavelength >= 380 && wavelength < 450 { print("It's violet!") } else if wavelength >= 590 && wavelength < 620 { print("It's orange!") } You can imagine that if we use an if statement to check the wavelengths of violet, blue, green, yellow, orange and red colors, the conditional becomes quite messy. Instead, we do this with the switch statement: let wavelength = 620 switch wavelength { case 380..<450: print("Purple!") case 450..<495: print("Blue!") case 495..<570: print("Green!") case 570..<590: print("Yellow!") case 590..<620: print("Orange!") case 620..<750: print("Red!") default: print("Not in visible spectrum") }
See what happens here? For every range of wavelengths we define an interval with the half-open range operator a..
This is where it gets interesting! You can use the switch statement in Swift with tuples.
A tuple is a simple ordered list of two or more values. It’s written between parentheses, like this:
let flight = (747, “SFO”)
The type of flight is (Int, String). Once a tuple is defined, you can’t change its order or type. Tuples are ideal for passing several associated values in one variable.
You can access the values of a tuple with an index number, like this:
print(flight.1)
// Output: SFO
It’s more convenient to name the tuple’s values. Like this:
let flight = (airplane: 747, airport: “SFO”)
print(flgith.airplane)
// Output: 747
You can use tuples together with switch statements. Like this:
for i in 1…100
{
switch (i % 3 == 0, i % 5 == 0)
{
case (true, false):
print(“Fizz”)
case (false, true):
print(“Buzz”)
case (true, true):
print(“FizzBuzz”)
default:
print(i)
}
}
The above code is part of a coding challenge called FizzBuzz.
In the above code we’re creating a tuple from two values, the result of i % 3 == 0 and i % 5 == 0. These are boolean operations, so the type of the tuple is (true, true).
We can now respond to different tuple values, such as (true, false) and (true, true). As such, this happens in the code:
Because we’re using switch to evaluate a tuple, we can react to different matching values in the tuple.
Here, check this out:
let airplane = (type: “737-800”, heading: “LAX”)
switch airplane {
case (“737-800”, _):
print(“This airplane is a 737-800 model.”)
case (let type, “LAX”):
print(“This \(type) is headed for Los Angeles Intl. Airport”)
default:
print(“Unknown airplane and heading.”)
}
In the above code we’re responding to 3 different cases:
Consider what happens in the above scenario, where the value of airplane is (type: “737-800”, heading: “LAX”). Which of the 3 cases is executed?
It’s the first case, because that’s the first case that matches. The type of the airplane is 373-800. Even though the LAX heading could potentially match the second case, the (“737-800”, _) is matched earlier. Don’t forget that your code executes line by line, from top to bottom!
Then, consider what happens when the value of airplane is (type: “747”, heading: “LAX”). Now the second case is executed, because the heading is LAX.
The let type in case (let type, “LAX”): is called a value binding. This temporarily binds the value from the tuple to a constant type. Within the switch case you can now use that constant, such as printing it out. Neat!
OK, there’s one last way of using the switch statement that you have to get to know. It’s by using where.
Here, check out the following Swift code:
enum Response {
case error(Int, String)
case success
}
let httpResponse = Response.success
switch httpResponse {
case .error(let code, let status) where code > 399:
print(“HTTP Error: \(code) \(status)”)
case .error:
print(“HTTP Request failed”)
case .success:
print(“HTTP Request was successful”)
}
// Output: HTTP Request was successful
In the above code, two things happen:
Consider what happens when you change the value of httpResponse to:
let httpResponse = Response.error(404, “Not Found”)
// Output: HTTP Error: 404 Not Found
And to:
let httpResponse = Response.error(301, “Moved Permanently”)
// Output: HTTP Request failed
What happens there?
There’s one .error case that we’re particularly interested in, namely an error code that’s greater than 399.
Here’s the relevant code:
case .error(let code, let status) where code > 399:
print(“HTTP Error: \(code) \(status)”)
The official HTTP Status Codes, which are used around the internet, indicate that status codes from 400 and up are client and server errors. Differently said, when you get one of those, something went wrong.
In the above code, we’re binding the code and status constants to the associated values of the Response enumeration. We’re also using the where keyword to indicate that this case should execute for any value of code that’s greater than 399.
You can almost literally read that as “cases of error where code is greater than 399”. It’s quite intuitive, and clearly defines that we’re specially interested in those error codes.
The if statement isn’t the only way to make decisions in your code. The switch statement provides numerous ways to consider a value, and to compare it with matching patterns.
And it’s especially powerful when combined with tuples, pattern matching, ranges, value bindings and where!
ncG1vNJzZmivp6x7orzPsqeinV6YvK570rCgrZuYYsC4tcWtZKGnp2LBsHs%3D