I find it hard to remember the different ways you can use case
with Swift beyond the familiar switch
. Here is one useful example for when you need to do something with the non-nil values in an array of optionals.
Here is the situation. I have an array of String
items some of which can be nil
.
let names = ["Tom", nil, "Fred"]
The type of names is [String?]
- an array of optional strings. Suppose I want to do some operation on each of the non-nil values in the array:
func doSomething(_ name: String) {
print(name)
}
My function wants a non-optional String
. I can iterate over the array grabbing the non-nil values with an if let
:
for name in names {
if let name = name { doSomething(name) }
}
// Tom
// Fred
This is a good time to remember about flatMap
to skip over the nil
values:
for name in names.flatMap({ $0 }) {
doSomething(name)
}
// Tom
// Fred
That is not bad but I like an alternate approach using case
with optional pattern matching:
for case let name? in names {
doSomething(name)
}
// Tom
// Fred
This works as optionals are an enum
with two cases: .none
and .some(T)
. The optional pattern (let name?
) is a shorthand for matching the .some
case which we could write as follows:
for case .some(let name) in names {
doSomething(name)
}
The pattern for matching nil
elements would be this:
for case .none in names {
print("found nil")
}
You need the ?
to make the pattern match optionals. So to match a specific value:
for case "Fred"? in names {
print("Found Fred")
}
Using where
with case
You can go one step further and add a where
clause to act as filter on the array.
let scores = [1,5,8,3,10,nil,7]
for case let score? in scores where score > 6 {
print(score)
}
// 8
// 10
// 7
Which I prefer to playing around with flatMap to remove the nil values before applying a filter:
for score in scores.flatMap({ $0 }).filter({ $0 > 6 }) {
print(score)
}