Swift enum pattern matching with extra conditions

In this post we are going to explore how we can provide more precise conditions for pattern matching when working with enums in Swift. The most common use case is using switch statements with the where clause to get more control over case conditions. But we'll also look into using the where clause in for-in loops to avoid unnecessary extra iterations. And finally, we'll see how to add extra conditions in while loops and if-case statements when the where clause is not available.

Let's consider an enum TransportationEvent representing different types of events in a transportation system.

enum TransportationEvent {
    case busArrival(busNumber: Int, passengers: Int)
    case trainArrival(trainNumber: Int, passengers: Int, cargoLoad: Int)
    case bicycleArrival
}

Now, imagine we have a system that handles these events. A basic switch statement might look like this.

let event = TransportationEvent.busArrival(busNumber: 42, passengers: 15)

switch event {
case .busArrival(let busNumber, let passengers):
    print("Bus \(busNumber) arrived with \(passengers) passengers.")
case .trainArrival(let trainNumber, let passengers, let cargoLoad):
    print("Train \(trainNumber) arrived with \(passengers) passengers and \(cargoLoad) tons of cargo.")
case .bicycleArrival:
    print("A bicycle arrived.")
}

This works, but what if we want to differentiate events based on the number of passengers or the amount of cargo? That's when we'd need to add some extra conditions.

where clause in switch statements

We can utilize the where clause to add specific conditions within our cases in a switch statement in Swift. In our TransportationEvent enum example it can help us to differentiate events based on the number of passengers or the amount of cargo.

switch event {
case .busArrival(let busNumber, let passengers) where passengers < 10:
    print("Bus \(busNumber) arrived with only a few passengers.")
case .busArrival(let busNumber, let passengers):
    print("Bus \(busNumber) arrived with \(passengers) passengers.")
case .trainArrival(let trainNumber, let passengers, let cargoLoad) where cargoLoad > 50:
    print("Train \(trainNumber) arrived with \(passengers) carrying a heavy cargo load.")
case .trainArrival(let trainNumber, let passengers, let cargoLoad):
    print("Train \(trainNumber) arrived with \(passengers) passengers and \(cargoLoad) tons of cargo.")
case .bicycleArrival:
    print("A bicycle arrived.")
}

where clause in for-in loops

In addition to switch statements, the where clause can also be leveraged in a for-in loop to iterate over elements that meet specific conditions. When iterating through a collection or sequence, the where clause enables us to filter the elements and iterate only over those that satisfy particular criteria. This allows us to process or manipulate the desired subset of elements within the loop.

For example, let's consider the TransportationEvent enum we've been working with. Suppose we have an array of transportation events, and we want to iterate over only the busArrival events where the number of passengers is greater than 10. We can achieve this by using the where clause in a for-in loop as follows.

let transportationEvents: [TransportationEvent] = [
    .busArrival(busNumber: 1, passengers: 5),
    .busArrival(busNumber: 2, passengers: 15),
    .trainArrival(trainNumber: 10, passengers: 50, cargoLoad: 100),
    .busArrival(busNumber: 3, passengers: 20),
    .bicycleArrival
]

for case let .busArrival(busNumber, passengers) in transportationEvents where passengers > 10 {
    print("Bus \(busNumber) arrived with \(passengers) passengers.")
}

In this example, the where clause is used in the for-in loop to filter out the busArrival events where the number of passengers is greater than 10. Only the busArrival events with more than 10 passengers will be processed within the loop.

Extra conditions in while loops

To add extra conditions in while loops we can use the comma syntax instead of the where clause. The comma essentially behaves like a logical AND operator, ensuring all conditions are met.

So if we want to carry on processing transportation events while they are bus arrivals with some passengers and stop as soon as our conditions are not met, we can write the following loop.

var index = 0
while case let .busArrival(busNumber, passengers) = transportationEvents[index], passengers > 0 {
    print("Processing bus \(busNumber) arrival with \(passengers) passengers.")
    index += 1
}

In this example, the while loop specifies two conditions separated by a comma. The first one checks if the current element at transportationEvents[index] matches the busArrival case, and the second one ensures that the number of passengers is greater than 0. Only while both conditions are met, the loop will iterate and process the event.

Extra conditions in if-case statements

If we need to add extra conditions in control flow statements, such as if-case, we can use the same comma syntax we used with a while loop. Let's say we want to check if a single transportation event is a busArrival with more than 10 passengers. We can accomplish this using the if-case statement, as demonstrated below.

let event = TransportationEvent.busArrival(busNumber: 42, passengers: 15)

if case let .busArrival(busNumber, passengers) = event, passengers > 10 {
    print("Processing bus \(busNumber) arrival with \(passengers) passengers.")
}

The comma-separated condition ensures that the busArrival event has more than 10 passengers before executing the code block. This allows us to conditionally handle specific scenarios based on our pattern matching criteria.


With the techniques we explored in this post, we can add a layer of flexibility and expressiveness to our pattern matching cases in Swift. Whether we are using switch statements, loops or control flow statements like if-case, we can handle complex scenarios with precision and clarity.

Integrating SwiftUI into UIKit Apps by Natalia Panferova book coverIntegrating SwiftUI into UIKit Apps by Natalia Panferova book cover

Check out our book!

Integrating SwiftUI into UIKit Apps

Integrating SwiftUI intoUIKit Apps

UPDATED FOR iOS 17!

A detailed guide on gradually adopting SwiftUI in UIKit projects.

  • Discover various ways to add SwiftUI views to existing UIKit projects
  • Use Xcode previews when designing and building UI
  • Update your UIKit apps with new features such as Swift Charts and Lock Screen widgets
  • Migrate larger parts of your apps to SwiftUI while reusing views and controllers built in UIKit