Swift Non-Nil Values In An Array Of Optionals

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)
}