Swift If Case Let

The Swift If-Case-Let syntax is a convenient shortcut to avoid a full switch statement when you only want to match a single case. The syntax always seems backwards to me so here’s a reminder of how to use it.

Case Let - A Recap

I’m using the Swift Result type to capture the results of a throwing expression. In this case, loading data from a file:

let result = Result { try Data(contentsOf: url) }
// result is of type Result<Data, Error>

The Result type is an enum with a success and failure case both of which have an associated value. In my example, I would typically switch on the result:

switch result {
  case .success(let data):
    completion(data)

  case .failure(let error):
    print(error)
}

The let binds the associated value to a variable that we can use in the body of the case. We can also write these as case let:

switch result {
  case let .success(data):
    completion(data)

  case let .failure(error):
    print(error)
}

We can also add a where clause to further filter the matching cases:

  case let .success(data) where data.count > 100:

The case let syntax is not restricted to switch statements. We can rewrite the switch as a series of if-case-let statements if we want:

// if statement
if case let .success(data) = result { ... }

// if statement - alternate form
if case .success(let data) = result { ... }

// if statement with extra condition
if case let .success(data) = result, data.count > 100 { ... }

Note that the value we were switching on (result) now becomes an initializer for the case statement. The last example written as a guard statement:

// guard statement
guard case let .success(data) = result, data.count > 100 else { 
  return
}

Finally, here’s an example of a for-in loop:

// for-in loop
for case let score in scores where score > 4 { ... }

Replacing A Switch With If Case Let

A switch statement is fine when I have something to do for all (or most) of the cases. Here’s an example where I only care about the failure case. I’m encoding some model data and then writing it to a property list file:

let result = PropertyListEncoder.encode(countries)
            .flatMap { Data.write($0, to: url) }

The Data.write method is an extension I added on Data that converts the throwing data.write(to:) method to return a result of type Result<void,Error>. It either succeeds, returning void, or fails with an error. Switching over the result in this case is annoying:

switch result {
case .success():
  // Nothing to do here

case .failure(let error):
  // Handle error
  print(error)
}

Using the case-if-let syntax to replace the switch statement:

if case let .failure(error) = result {
  // Handle error
  print(error)
}

Or using the alternate version which I find slightly easier to remember as it’s closer to the original switch syntax:

if case .failure(let error) = result {
  // Handle error
  print(error)
}

I still find using the result value as the initializer for the case-let confusing but I prefer it over the more verbose switch version. What do you think? Do you have a favorite use for case let?