Switch statements compare specific values against different cases and execute the block of code associated with the first matching case. You can achieve the same result using an if statement with multiple else clauses, but switch statements are easier to read and understand at a glance.

This article covers the most common use cases of switch statements. There are other, more advanced features of switch statements that I will talk about in future articles.

- This article is part of the
**Learn Swift**series.

## Architecting SwiftUI apps with MVC and MVVM

GET THE FREE BOOK NOW## Table of contents

- Make switch statements exhaustive with default cases
- Add break statements to default switch cases if default cases don’t need to do anything else
- Control the natural flow of switch statements with fallthrough statements
- Cover an unlimited number of values in switch cases with ranges
- Cover only a specific set of switch cases with tuples of booleans

## Make switch statements exhaustive with default cases

*Switch statements* cover all of the possible cases out there. You should define your *default cases *to ensure that your switch statements are always *exhaustive.*

We can use a number guessing game as an example. It uses switch statements to determine information about a pair of whole numbers based on the exact difference between them.

```
let first = 12
let second = 10
let difference = first - second
switch difference {
case 0: print("\(first) is equal to \(second) and they are both either odd or even numbers.")
case 1: print("\(first) is greater than \(second) and they are both odd and even consecutive numbers.")
case 2: print("\(first) is greater than \(second) and they are both either odd or even consecutive numbers.")
default: print("\(first) and \(second) are both random numbers.")
}
```

The switch statement in this code handles only a few very particular values manually. The code that is executed if a specific case is `true`

is placed right after the colon. All cases are placed between curly brackets since they are all part of the switch statement.

The default case covers all of the other remaining values automatically. This ensures that the switch statement is exhaustive.

The switch statement handles cases manually if the first whole number in the pair is greater than or equal to the second one. You can add another switch statement covering all the other missing cases.

```
let first = 10
let second = 12
let difference = first - second
switch difference {
case -2: print("\(first) is less than \(second) and they are both either odd or even consecutive numbers.")
case -1: print("\(first) is less than \(second) and they are both odd and even consecutive numbers.")
case 0: print("\(first) is equal to \(second) and they are both either odd or even numbers.")
default: print("\(first) and \(second) are both random numbers.")
}
```

This switch statement handles values manually if the first whole number in the pair is less than or equal to the second one. The default case covers all of the other remaining values automatically as before.

You can simplify this by combining the two simple switch statements into a single complex one.

```
let first = 10
let second = 10
let difference = first - second
switch difference {
case -2: print("\(first) is less than \(second) and they are both either odd or even consecutive numbers.")
case -1: print("\(first) is less than \(second) and they are both odd and even consecutive numbers.")
case 0: print("\(first) is equal to \(second) and they are both either odd or even numbers.")
case 1: print("\(first) is greater than \(second) and they are both odd and even consecutive numbers.")
case 2: print("\(first) is greater than \(second) and they are both either odd or even consecutive numbers.")
default: print("\(first) and \(second) are both random numbers.")
}
```

This switch statement covers all of the cases handled by the previous switch statements. The default case works exactly as before too.

There are certain situations where default cases in switch statements don’t need to do anything else because all of the other cases have already been appropriately handled. In the next section of this article, you will learn what to do when this happens.

## Add break statements to default switch cases if default cases don’t need to do anything else

Default cases are always needed to make switch statements exhaustive. They can never be left empty even if they don’t do anything. The `break`

statement is a valuable tool to ensure this.

A multiple whole number generator finds the first multiple whole number of 3 that is greater than or equal to a specific whole number.

```
let random = 10
let remainder = random % 3
let multiple: Int
switch remainder {
case 0: multiple = random
case 1: multiple = random + 2
case 2: multiple = random + 1
default: break
}
```

This switch statement uses the remainder operator to generate the following multiple whole number. All of the possible remainder values are covered manually by the switch statement. The `break`

statement simply exits the default case since there are no other values to be handled automatically.

This switch statement only works for positive whole numbers. You can add another switch statement which works only for whole negative numbers.

```
let random = -10
let remainder = random % -3
let multiple: Int
switch remainder {
case -2: multiple = random + 2
case -1: multiple = random + 1
case 0: multiple = random
default: break
}
```

This switch statement uses the *remainder operator* with whole negative numbers to generate the following multiple whole number as before. The `break`

statement also works in the same way in the default case of the switch statement.

The modulo operator always returns the same result for both the `a % b`

and `a % -b`

operations for any given `a`

and `b`

positive and negative whole numbers. This is how the *modulo operator *works by default* *in Swift.

```
10 % 3 == 10 % -3 // true
-10 % 3 == -10 % -3 // true
```

This means that the previous two switch statements cover all possible cases, and, as a result, you can combine them into a single one.

```
let random = 10
let remainder = random % 3
let multiple: Int
switch remainder {
case -2, 1: multiple = random + 2
case -1, 2: multiple = random + 1
case 0: multiple = random
default: break
}
```

This switch statement uses *compound cases *to group related cases into one. Commas separate the values handled by the compound case since they are all part of the compound case*. *The `break` statement exits the default case of the switch statement as before.

There are scenarios where it is helpful to enable switch statements to go from a particular case to another instead of stopping at the first valid case as they otherwise do by default. We will look at how to do this next.

## Control the natural flow of switch statements with fallthrough statements

Switch statements stop at the first valid case by default. You can change this default behavior to automatically make the switch statement jump from one case to the next.

For example, a random whole number generator uses both dice rolls and compound assignment operators to generate random whole numbers.

```
var result = 10
let dice = 2
switch dice {
case 1: result = -result
case 2: result += 2
case 3: result *= 3
case 4: result -= 4
case 5: result /= 5
case 6: result %= 6
default: break
}
```

This switch statement rolls the die and determines the corresponding random whole number associated with a specific *compound assignment operator *and the die’s number.

It would be nice to generate even more significant random whole numbers. You can do that by tweaking the switch statement just a little.

```
var result = 10
let dice = 2
switch dice {
case 1: result = -result
case 2: result += 2
result *= 3
case 3: result *= 3
case 4: result -= 4
case 5: result /= 5
case 6: result %= 6
default: break
}
```

The above switch statement adds the block of code associated with the `3`

case to the `2`

case. This generates a much bigger random whole number than before. Unfortunately, this violates the DRY principle because the same code is duplicated in both cases of the switch statement. You can quickly fix this by using functions instead.

```
var result = 10
let dice = 2
func multiply(_ number: Int, by multiplier: Int) -> Int {
number * multiplier
}
switch dice {
case 1: result = -result
case 2: result += 2
result = multiply(result, by: 3)
case 3: result = multiply(result, by: 3)
case 4: result -= dice
case 5: result /= dice
case 6: result %= dice
default: break
}
```

The `multiply(_:by:)`

function encapsulates the corresponding logic for multiplying a certain number by a given multiplier. If you take this approach, you should create different functions for all case transitions in switch statements. An easier way to handle things properly is to use `fallthrough`

statements instead.

```
var result = 10
let dice = 2
switch dice {
case 1: result = -result
case 2: result += 2
fallthrough
case 3: result *= 3
case 4: result -= 4
case 5: result /= 5
case 6: result %= 6
default: break
}
```

This `fallthrough`

statement automatically enables the switch statement to go from the `2`

case to the `3`

case.

You may use `fallthrough`

statements in as many cases of switch statements as you need.

```
var result = 10
let dice = 4
switch dice {
case 1: result = -result
case 2: result += 2
fallthrough
case 3: result *= 3
case 4: result -= 4
fallthrough
case 5: result /= 5
case 6: result %= 6
default: break
}
```

The second `fallthrough`

statement above enables the switch statement to go from the `4`

case to the `5`

case automatically as before. This generates a much smaller random whole number than before.

In certain situations, switch statements need to handle an unlimited number of cases. You will learn about these in the next section of this article.

## Cover an unlimited number of values in switch cases with ranges

Sometimes certain cases of switch statements should cover an unlimited number of values. You can easily do this with ranges that are intervals of whole numbers.

Switch statements use pattern matching techniques to determine which* range* a particular whole number belongs to.

```
0...10 ~= 10 // true
0..<10 ~= 10 // false
0... ~= 10 // true
...0 ~= 0 // true
..<0 ~= 0 // false
```

The `~=`

operator in this code is the *pattern matching operator*. It returns either `true`

if the range on its left side contains the value on its right side or `false`

otherwise.

A number guessing game uses switch statements with *ranges *to determine information about a whole number.

```
let number = 10
switch number {
case ...(-10): print("\(number) is a negative number.")
case -9..<0: print("\(number) is a negative digit.")
case 0...9: print("\(number) is a positive digit.")
case 10...: print("\(number) is a positive number.")
default: break
}
```

This switch statement uses the following types of ranges to go through all of the possible whole numbers out there:

- The
`…right`

range is the*one-sided range closed at its right side*. It contains only the right side of the range and all of the other whole numbers up to it.`...0 ~= 0 // true`

- The
`left..<right`

range is the*half-open range on its right side*. It contains only the left side of the range and all of the other whole numbers between the left side and up to the right side of the range.`0..<10 ~= 10 // false`

- The
`left…right`

range is the*closed range on both sides*. It contains the left and right sides of the range and the whole numbers between the two.

`0...10 ~= 10 // true`

- The
`left…`

range is the*one-sided range closed at its left side*. It contains only the left side of the range and all other whole numbers from there.`0... ~= 10 // true`

Switch statements can use one other type of range if they need to.

```
let number = 10
switch number {
case ..<(-9): print("\(number) is a negative number.")
case -9..<0: print("\(number) is a negative digit.")
case 0...9: print("\(number) is a positive digit.")
case 10...: print("\(number) is a positive number.")
default: break
}
```

The switch statement in this code uses the `..<right`

range, which is the *one-sided range half open at its right side*. It contains all the other whole numbers up to the right side of the range.

`..<0 ~= 0 // false`

The right side of any range should be placed between round brackets if and only if its right side is a negative number. This is so that the compiler knows that the minus sign isn’t part of the *range operator*.

```
...(-10) ~= -10 // true
..<(-9) ~= -10 // true
-20...(-10) ~= -10 // true
-20..<(-9) ~= -10 // true
```

The left side of both half-open ranges and closed ranges that aren’t one-sided must always be less than or equal to their right side.

```
10...0 ~= 10
10..<0 ~= 10
```

The compiler flags a runtime error for this code because the left sides of both ranges are greater than both of their right sides.

There are specific scenarios where switch statements don’t need any default cases. This is because they are exhaustive by default, as all possible cases are handled manually instead. You can learn about when this happens in the next section of this article.

## Cover only a specific set of switch cases with tuples of booleans

The easiest way to ensure switch statements are *exhaustive* by default is to use tuples of booleans to implement them. A *tuple *is simply an ordered list of different elements that make sense to group into a single data unit.* *

The login feature of an app checks whether the username and password of a user are valid. You can quickly test the user’s credentials with boolean tuples.

```
let validUser = true
let validPassword = true
switch (validUser, validPassword) {
case (true, true): print("Valid credentials!")
case (true, false): print("Invalid password!")
case (false, true): print("Invalid user!")
case (false, false): print("Invalid credentials!")
}
```

The switch statement in this code handles all possible cases since both `validUser`

and `validPassword`

are either `true`

or `false`

in this case. All cases define pairs of boolean values for the `(validUser, validPassword)`

boolean tuple. The constants and values of the tuple are placed between round brackets and separated by commas.

However, you can simplify the above switch statement by using compound cases.

```
let validUser = true
let validPassword = true
switch (validUser, validPassword) {
case (true, true): print("Valid credentials!")
case (true, false): print("Invalid password!")
case (false, true), (false, false): print("Invalid user!")
}
```

The login implementation of the app doesn’t care whether the user’s password is invalid or not as long as the user’s username is not.

You can simplify the previous switch statement further with *wildcards.*

```
let validUser = true
let validPassword = true
switch (validUser, validPassword) {
case (true, true): print("Valid credentials!")
case (true, false): print("Invalid password!")
case (false, _): print("Invalid user!")
}
```

The underscore in this code covers all possible values of `validPassword`

, which are both `true`

and `false`

.

## Conclusion

This article covers switch statements’ most common use cases with tuples and ranges. Future articles will build on this and show you how to refine your code with switch statements further.

## Architecting SwiftUI apps with MVC and MVVM

It’s easy to make an app by throwing some code together. But without best practices and robust architecture, you soon end up with unmanageable spaghetti code. In this guide I'll show you how to properly structure SwiftUI apps.

Cosmin has been writing tutorials and books on Swift and iOS development since 2014, when the first version of Swift was released. He has been part of influential iOS development communities such as raywenderlich.com and appcoda.com since 2010 and has taught iOS development with Objective-C and Swift since 2012. Cosmin lives in Romania and has engineering degrees in electronics, telecommunications, neural networks, microprocessors, microcontrollers, distributed systems, and web technologies from the technical universities of Cluj-Napoca and Iași. He has worked with many programming languages over the years, but none of them has had such a significant impact on himself as Swift. Cosmin likes to play the guitar or study WW2 history when not coding