# Swift 2: Control Flow Pattern Matching Examples

This article aims to provide examples of the expanded pattern matching support which ships with Swift 2.

## Pattern matching using `if` and `guard`

New in Swift 2 is support for pattern matching within `if` statements (and `guard` statements). Let’s first define a simple enum:

``````// A generic 'number' type that can represent either an integer, a
// floating-point value, or a boolean.
enum Number {
case IntegerValue(Int)
case DoubleValue(Double)
case BooleanValue(Bool)
}``````

### Checking for a certain case

Use case: we want to check whether a value corresponds to a certain case. This works regardless of whether the case has associated values or not, but does not unbox the values (if they exist).

``````let myNumber : Number = // ...

if case .IntegerValue = myNumber {
print("myNumber is an integer")
}``````

The pattern, `case .IntegerValue`, comes first, and the value to be matched, the variable `myNumber`, comes after the equals sign. This might seem counterintuitive, but it’s similar to how the optional-unwrapping `if let a = a` is written: the value that is being checked comes after the equals sign.

The equivalent Swift 1.2 version, using `switch`, follows:

``````switch myNumber {
case .IntegerValue: print ("myNumber is an integer")
default: break
}``````

### Unboxing an associated value

Use case: we want to check whether a value corresponds to a certain case, and if so we want to extract the associated value(s).

``````if case let .IntegerValue(theInt) = myNumber {
print("myNumber is the integer \(theInt)")
}``````

The pattern now becomes `case let .IntegerValue(theInt)`. The value to be matched remains the same as in the previous example.

Here is an example of the same concept, but applied to `guard`. The semantics of the predicates for `guard` and `if` are identical, and so pattern matching works exactly the same way.

``````func getObjectInArray<T>(array: [T], atIndex index: Number) -> T? {
guard case let .IntegerValue(idx) = index else {
print("This method only accepts integer arguments!")
return nil
}
return array[idx]
}``````

### Qualifying with `where` clauses

As with any `case` in a `switch` statement, a `where` clause can optionally follow a pattern to provide an additional constraint. Let’s modify the `getObjectInArray:atIndex:` function from the previous example:

``````func getObjectInArray<T>(array: [T], atIndex index: Number) -> T? {
guard case let .IntegerValue(idx) = index where idx >= 0 && idx < array.count else {
print("This method only accepts integer arguments that are in bounds!")
return nil
}
return array[idx]
}``````

### Complex `if` predicates

Swift’s `if` statement is surprisingly capable. An `if` statement can have multiple predicates, separated by commas. Predicates fall into one of three categories:

• Simple logical test (e.g. `foo == 10 || bar > baz`). There can only be one such predicate, and it must be placed first.
• Optional unwrapping (e.g. `let foo = maybeFoo where foo > 10`). If an optional unwrapping predicate immediately follows another optional unwrapping predicate, the `let` can be omitted. Can be fitted with a `where` qualifier.
• Pattern matching (e.g. `case let .Bar(something) = theValue`), as discussed above. Can be fitted with a `where` qualifier.

Predicates are evaluated in the order they are defined, and no predicates after a failing predicate will be evaluated. Here is a (contrived) example of a complicated `if` statement.

``````let a : Int = // ...
let b : Int = // ...
let firstValue : Number? = // ...
let secondValue : Number? = // ...

if a != b,
let firstValue = firstValue,
secondValue = secondValue,
case let .IntegerValue(first) = firstValue,
case let .IntegerValue(second) = secondValue where second > first {
print("a and b are different, and secondValue is greater than firstValue")
print("a + b + firstValue + secondValue = \(a + b + first + second)")
}``````

As always, prioritize intelligibility over cleverness when building such constructs.

## Pattern matching using `for`-`in`

Pattern matching can be used in conjunction with the `for`-`in` loop. In this case the intent is to iterate over the items within a sequence, but only those items which match the given pattern. An example follows:

``````enum EngineeringField : String {
case Civil, Mechanical, Electrical, Chemical, Nuclear
}

enum Entity {
case EngineeringStudent(name: String, year: Int, dept: EngineeringField)
case HumanitiesStudent(name: String, year: Int, dept: String)
case EngineeringProf(name: String, dept: EngineeringField)
case HumanitiesProf(name: String, dept: String)
}

// A list of entities currently present in the classroom.
var currentlyPresent = [
Entity.EngineeringProf(name: "Alice", dept: .Mechanical),
Entity.EngineeringStudent(name: "Belinda", year: 2016, dept: .Mechanical),
Entity.EngineeringStudent(name: "Charlie", year: 2017, dept: .Chemical),
Entity.HumanitiesStudent(name: "David", year: 2017, dept: "English Literature"),
Entity.HumanitiesStudent(name: "Evelyn", year: 2018, dept: "Philosophy"),
Entity.EngineeringStudent(name: "Farhad", year: 2017, dept: .Mechanical)
]

// Only iterate over the engineering students
for case let .EngineeringStudent(name, _, dept) in currentlyPresent {
print("\(name), who studies \(dept) Engineering, is present.")
}
/* Output:
Belinda, who studies Mechanical Engineering, is present.
Charlie, who studies Chemical Engineering, is present.
Farhad, who studies Mechanical Engineering, is present.
*/``````

In the `for`-`in` predicate, the pattern each element is compared against precedes the `in` keyword, and the sequence to iterate over follows.

Note that, as with patterns in `switch` statements, we can unwrap multiple associated values, and ignore those we don’t care about using `_`. We could, if necessary, also add a `where` clause with an additional condition to further constrain which elements we iterate over.

## Pattern matching using `while`

Pattern matching can also be used in conjunction with the `while` loop. In this case the intent is to repeatedly run the loop body so long as some value in the predicate successfully matches a pattern. To wit:

``````enum Status {
case Continue(Int)
case Finished
}

func doSomething(input: Int) -> Status {
if input > 5 {
return .Finished
} else {
// Do work
print("Doing work with input \(input)")
return .Continue(input + 1)
}
}

var latestStatus = doSomething(1)
while case let .Continue(nextInput) = latestStatus {
latestStatus = doSomething(nextInput)
}
/* Output:
Doing work with input 1
Doing work with input 2
Doing work with input 3
Doing work with input 4
Doing work with input 5
*/``````

Note that the compound predicates described above in Complex if predicates are also supported by the `while` loop, including use of `where` clauses.

A brief overview of some of the other patterns supported by the pattern matching mechanism follows.

### Optional pattern

The new optional pattern, `myValue?`, is equivalent to `.Some(myValue)` for optionals. (Note that you can use these patterns within a `switch` statement’s `case` as well, like with all other patterns.)

``````// Refer to the 'Number' enum from before.
let myValue : Number? = // ...

if case .IntegerValue? = myValue {
print("myValue was not nil, it is some unspecified integer")
}

if case let .IntegerValue(theInt)? = myValue {
print("myValue was not nil, and it contained the integer \(theInt)")
}``````

### Nested enumerations

As before, patterns can be nested, including enumeration patterns. The previous examples could also be written as:

``````if case .Some(.IntegerValue) = myValue {
print("myValue was not nil, it was some unspecified integer")
}

if case let .Some(.IntegerValue(theInt)) = myValue {
print("myValue was not nil, and it contained the integer \(theInt)")
}``````

### Type checking patterns

The type checking patterns `is` and `as` can be used to determine:

• Is an instance of the protocol type `T` actually an instance of the concrete type `U` (where `U` conforms to `T`)?
• Is an instance of the class `T` actually an instance of the class `U` (where `U` is a subclass of `T`)?
``````class Animal {
var age : Int
init(age: Int) { self.age = age }
}

class Cat : Animal {
var hasFur : Bool, name : String
init(age: Int, name: String, hasFur: Bool) {
self.hasFur = hasFur
self.name = name
super.init(age: age)
}
}

class Dog : Animal { /* ... */ }

let myPet : Animal = // ...

// 'is' if we only care about whether the downcast succeeded
if case is Dog = myPet {
print("myPet is a Dog.")
}

// 'as' is we want a reference to the value as the downcasted type
if case let myCat as Cat = myPet {
print("myPet is a Cat named \(myCat.name) who is \(hasFur ? "not" : "") a Sphynx cat")
}``````

Unsurprisingly, we can nest type checking patterns within other patterns, such as the enumeration patterns described earlier.

``````enum Thing {
case SomeAnimal(Animal), SomeVegetable, SomeMineral
}

let anObject : Thing = // ...

if case .SomeAnimal(is Cat) = anObject {
print("anObject is a SomeAnimal containing a Cat")
}

if case let .SomeAnimal(theCat as Cat) = anObject {
print("anObject contains a \(theCat.age)-year-old Cat named \(theCat.name)")
}``````

### Other patterns

At this point it should hopefully be clear how the other patterns Swift supports can be utilized in conjunction with the control flow statements. For example, Swift supports matching on ranges:

``````let myNumber = 50

if case 0..<100 = myNumber {
print("myNumber is between 0 and 99")
}``````

It also supports matching on expression patterns (the behavior of which defaults to comparison by `==`, and can be customized by overriding `~=`):

``````let protagonist = "jyaku"

if case "jyaku" = protagonist {
print("you could have used a plain old 'if' statement for this")
}``````

Theoretically, you should be able to match tuple patterns, but (as of Xcode 7.1β2) the compiler segfaults instead:

``````let a = true
let b = true

if case (true, true) = (a, b) {
print("both 'a' and 'b' are true")
}``````

## To `switch` or not to `switch`?

Before Swift 2, pattern matching showed up in only two places: multiple variable assignment, and the `switch` statement. If any of an enumeration’s cases contained associated values, `switch` was the only way to manipulate instances of that enum. The exception to this was `Optional`, for which the special `if let` syntax was devised.

Swift 2 grants to all enums the power of `if let`, and much more. Pattern matching and control flow logic work together, meaning that one no longer needs to (for example) write `for`-`in` loops whose bodies consist of a single `switch` statement, or alternate nesting `if` and `switch` statements.

Pattern matching and control flow also obviate the need for a number of patterns. For example, it was sometimes desirable to add ‘accessor’ properties to enums to retrieve the associated values for specific cases:

``````extension Number {
var valueAsInteger : Int? {
switch self {
case let .IntegerValue(value): return value
default: return nil
}
}

var valueAsBoolean : Bool? {
switch self {
case let .BooleanValue(value): return value
default: return nil
}
}

// ...
}``````

This way, you could use `if let` instead of `switch` if one really only cared about whether an enum instance was one specific case or not:

``````func getObjectInArray<T>(array: [T], atIndex index: Number) -> T? {
switch index {
case let .IntegerValue(index): return array[index]
default: return nil
}
}

// became...

func getObjectInArray<T>(array: [T], atIndex index: Number) -> T? {
if let index = index.valueAsInteger {
return array[index]
}
return nil
}``````

With Swift 2, such boilerplate is no longer necessary.

However, `switch` statements still have their uses, the most important of which is probably exhaustiveness checking. For enumerations with more than three cases, `switch` statements still provide the ability to check that all cases were covered, something that `if`, `for`-`in`, and `while` cannot.

This is helpful if you later go back and add cases to your enum; the compiler will flag every place you switch against that enum (unless you were lazy and used `default` or `case _` needlessly). In a large project where an enum may be switched against in hundreds or thousands of places, this feature can mean the difference between introducing undiscoverable bugs when refactoring or not.

Thanks to @gsimmons for catching a typo in the sample code.
{"ft_lang_label":"__label__en","ft_lang_prob":0.76627874,"math_prob":0.8638622,"size":12036,"snap":"2020-24-2020-29","text_gpt3_token_len":2854,"char_repetition_ratio":0.12350399,"word_repetition_ratio":0.082051285,"special_character_ratio":0.24202393,"punctuation_ratio":0.16757742,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.97435755,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2020-06-07T01:39:24Z\",\"WARC-Record-ID\":\"<urn:uuid:95891671-223a-4a19-a2da-8c254acdabe6>\",\"Content-Length\":\"41408\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:9ee34b87-90d2-4838-9b48-0fc194ac1ca5>\",\"WARC-Concurrent-To\":\"<urn:uuid:a3c5e41c-d742-4105-87b8-11e1b85d42c7>\",\"WARC-IP-Address\":\"192.30.252.153\",\"WARC-Target-URI\":\"http://austinzheng.com/page3/\",\"WARC-Payload-Digest\":\"sha1:L77RIZSQDP4HV7Y2C4X2T5N4MUB53TUO\",\"WARC-Block-Digest\":\"sha1:XXRDHOE5PWRFGHCZE7DFTCONVMXIFFHT\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2020/CC-MAIN-2020-24/CC-MAIN-2020-24_segments_1590348523476.97_warc_CC-MAIN-20200607013327-20200607043327-00330.warc.gz\"}"}