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
?