Leave a rating/review
Notes: 24. More Switch Statements
Update Notes: This course was originally recorded in 2019. It has been reviewed and all content and materials updated as of October 2021.
You’ve seen how easy it is to use a switch statement with enumerations. But what about other types of values? If you find yourself creating long if statements with lots of else clauses, a switch statement might be a better choice. Let’s see what else you can do with switch statements, to help you decide.
- Let’s start with a function that will return a description for a given number
func getDescription(for number: Int) -> String {
}
Inside the function body, start a switch statement that switches on that number parameter
switch number {
}
As the error tells you, we need to do at least …something… in the switch statement. We don’t have an enumeration to help us out this time, but this error can still do something for us!
By clicking on the red and white octagon, and then, the Fix button, you’ll get the keyword default, followed by a colon. This is called the “default case”.
Cases can’t be empty, so, we still need to put some code in here! The simplest thing you can do is type break, which will break out of the scope of the switch statement.
break
break can be useful! That’s what you’d use anytime you want nothing to happen given a specific value.
But we actually want to return a description from this function, so let’s say the default would be “No Description”
return "No Description"
Now our function should return that string no matter what we pass into it. Call it to try it out!
getDescription(for: 15)
Run the playground and we’ve got “No Description” over on the side.
Let’s make a different assignment for the “case” of number being zero. You want default to be the last case, so add this new case above it:
case 0:
return "Zero"
Test that by calling the function and passing in 0
getDescription(for: 0)
And when you run things again, you get “Zero” for 0, but you still get “No Description” for 15!
For number types, like Int and Double, it’s very easy to find out if what you’re switching on is within a range.
case 1...9:
return "Between 1 and 9"
Test that by passing in anything between 1 and 9.
getDescription(for: 4)
We can also do something called value binding. After the case keyword, use let, and a new name for the value, that’s more specific. In this case, let’s check for a “negativeNumber”.
case let negativeNumber
Then, before the colon, you can use a where clause. Here, we’ll check to see if negativeNumber is less than zero, and return the word “Negative” if it is.
case let negativeNumber😺 where negativeNumber < 0:
return "Negative"
Now if you pass in a negative number…
getDescription(for: -52)
You get “Negative”! We can also use a where clause without binding to a new value. That works very much like the previous case, except you use an underscore:
42 case _ where number > .max / 2:
numberDescription = "Very large!"
Here, we’ve used number itself, to check for very large numbers. The underscore is saying “I don’t want to give a name to that value”. Try passing Int.max, the largest Int you can have, into the function
getDescription(for: Int.max)
Aaannnndd it’s “Very Large!”.
So far, we’ve been switching on values, but you can also switch on expressions. Declare two constants: an Int called number and a boolean that says whether the number is even.
let number = Int.max
let numberIsEven: Bool
To assign numberIsEven, we can switch on an expression, like the remainder of number divided by 2.
switch number % 2 {
}
In the case of zero, numberIsEven is true.
case 0:
numberIsEven = true
The only other case that can happen is 1, but the compiler is not wise enough to know that, so instead, we just use default for odd numbers.
default:
numberIsEven = false
Run the playground and it turns out the biggest Int you can have isn’t an even number.
Switching on an expression, like we’ve done here, can help to simplify your switch statements. You might be able to avoid binding or where clauses. And then, your switch statements might be easier to read. And that might mean it’s harder to write bugs by accident!
Now, we’ve gone over the tools at your disposal, for one value. But you can apply them to multiple values as well, using tuples!
Let’s start with a function that takes in a tuple of two Doubles, those will represent a pair of x/y coordinates for a point
func pointCategory(for coordinates: (Double, Double))
and returns a string that represents that point’s category.
func pointCategory(for coordinates: (Double, Double))😺 -> String {
}
Inside the function, switch on coordinates, and return “No Category” by default
switch coordinates {
default:
return "No Category"
}
If we just want to check a value directly, like the origin, we can type that literal tuple, in a case.
71 case (0, 0):
return "Origin"
These zeros are implicitly doubles, not integers, even though they don’t have a dot.
The compiler knows the type because we’ve said coordinates is made up of Doubles, up here in the parameter list.
You can also make comparisons differently, across the tuple elements. For example, we can bind a value to just the first coordinate:
case (let x, 0):
return "On the x-axis at \(x)"
Here, we’re calling that first value x, and then printing where, along the x-axis, the value is.
And we can do the same thing, for the y-coordinate.
case (0, let y):
reutrn "On the y-axis at \(y)"
Try all of those out with some function calls…
pointCategory(for: (0, 0))
pointCategory(for: (50, 0))
pointCategory(for: (0, 3))
…and we’ve got something at the origin, on the x-axis, and on the y-axis! You can also give a name to multiple parts of a tuple at once.
case let (x, y):
return "No zero coordinates. x = \(x), y = \(y)"
And try that out with a combo like -4 and 17
pointCategory(for: (-4, 17))
Something you might have worked out, but I haven’t explicitly told you yet, is that cases are evaluated in order.
If you were to move the previous two cases below this one,
and then run the playground again, you’d get an inaccurate message! So, undo that and move the cases back into position.
Also, it is possible to make a Switch Statement exhaustive without the default case, even if you aren’t switch on an enumeration.
Because this case where we’re binding both tuple elements can exhaust every possibility, we actually don’t need the default case anymore.So, delete it!
Lastly, as you might have guessed, you can employ where clauses for tuples. Like the special case, where y is equal to x squared.
case let (x, y) where y == x * x:
pointCategory = "Along y = x ^ 2"
Try passing in (2, 4) for coordinates
pointCategory(for: (2, 4))
And just above that case, if you don’t want to bind values, you can use underscores, either within the tuple…
😺case (_, let y) where coordinates.0 == y:
pointCategory = "Along y = x"🛑
case let (x, y) where y == x * x
…or, instead of the entire tuple.
77 case _ where coordinates.0 == coordinates.1:
pointCategory = "Along y = x"
Give that one a try as well. Pass in any matching numbers other than 0.
pointCategory(for: (6, 6))
That’s a rundown of the features you’re likely to come across when reading switch statements in Swift. In the upcoming challenge, you’ll get some practice writing them!