if case let in Swift explained

Published on: April 26, 2024

In Swift, we can use the case keyword in multiple places. Most commonly, a case is used in switched but since you’re here, you might have seen a case in combination with an if statement.

In this post, we’ll explore different places where we can use the case keyword to perform something called pattern matching in Swift.

Pattern matching is a powerful feature of Swift that allows us to perform highly elegant checks to see if a given type matches a certain value.

In this post, we’ll explore a specific kind of pattern matching in Swift; the if case let approach of pattern matching.

Understanding pattern matching

The syntax for if case let is somewhat complex. So let’s start with a quick code sample that demonstrates how you can write an if statement that attempts to match an enum case:

enum ShapeType {
  case rectangle, triangle, circle

let myShape = ShapeType.rectangle

if case .rectangle = myShape {
  print("myShape is a rectangle")

Now, let me start by saying we didn’t need to use the case syntax here. We could have just as well written the following:

if myShape == .rectangle {
  print("myShape is a rectangle")

However, I like the earlier example because it introduces the case syntax in a pretty clean way.

Now, before I dig in to show you the case let syntax I’d like to take a look at the form of pattern matching in Swift that’s most likely the one you’re most familiar with:

switch myShape {
case .rectangle:
  print("myShape is a rectangle")
case .triangle:
case .circle:

A switch in programming allows us to write a list of patterns that we want to compare a given value to. This is much more convenient that writing a bunch of if / else statements.

The case keyword in Swift does not perform any special magic. Instead, it invokes a special operator that compares our pattern (whatever we write after case) to a value (the value we’re switching over in a switch).

So… how does that help you understand if case let syntax?

Understanding if case let

Once you know that if case .rectangle = myShape invokes a comparison between .rectangle and myShape the following suddenly makes a little more sense:

enum LoadingState {
  case inProgress(Task<String, Never>)
  case loaded(String)

let state = LoadingState.loaded("Hello, world")

if case .loaded(let string) = state {
  print("Loaded string is \(string)")

// or

if case let .loaded(string) = state {
  print("Loaded string is \(string)")

In both comparisons, we compare our enum case of .loaded and we assign its associated value to a constant. I prefer case .loaded(let string) myself because it looks a little less strange that case let .loaded(string) but they’re functionally equivalent.

And in a switch, you’d use the same patterns to match against which always helps me to remember:

switch state {
case .inProgress(let task):
case .loaded(let string):
  print("Loaded string is \(string)")

Again, the pattern here is that we compare our case to a value. In a switch this looks a lot more natural than it does in an if statement but they’re the same under the hood and they both use the underlying ~= comparator.

That said, writing if case .loaded(let string) = state when you’re only interested in a single case is certainly more convenient than writing a full blown switch when you’re only interested in a single case.

Subscribe to my newsletter