I'm sure [your] internal document would have been much useful.
Pack the bags it’s guilt trip time! (-:
Seriously though, I had some time this week so I decided to tidy this up and post it here. And, on the plus side, creating and verifying all the examples below gave me a much better understanding of how patterns work (-:
IMPORTANT This isn’t an “internal document” but rather a cheat sheet that I created for my own personal use. I hope you find it useful too, but be aware that it isn’t an official Apple document, hasn’t been formally reviewed, and if it contains any mistakes then they’re all my fault!
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
There are eight types of patterns, summarised in the table below.
|
Type | Kind | Type Annotation? | Syntax Summary |
wildcard | destructuring | optional | _ |
identifier | destructuring | optional | identifier |
value binding | [1] | no | ( var |
tuple | [1] | optional | ( ( [ identifier : ] pattern )… ) |
enumeration case | matching | no | [ type-identifier ] . enum-case-name [ tuple-pattern ] |
optional | matching | no | identifier ? |
type casting | matching | no | is type |
expression | matching | no | expression |
Notes
- These patterns are destructuring if any of their sub-patterns are destructuring.
Destructuring patterns allow you to pull apart a structured value. The can be used in any context. In contrast, matching patterns can only be used in:
- The case of a
switch
statement - The
catch
clause of a do
statement - (
if
| while
| guard
| for
) case
statements
A type annotation is a colon followed by a type. For example, consider the following:
let answer: Int = 42
^^^^ initializer
^^^^^ type-annotation
^^^^^^ identifier-pattern
^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^ constant-declaration
Note In these examples the syntax terms are taken directly from The Swift Programming Language.
A common use for a wildcard pattern is in a switch
statement where you want to do a wildcard match for all possible values.
switch (indexPath.section, indexPath.row) {
case (0, 0): … handle a fixed item in section 0 …
case (0, 1): … handle a fixed item in section 0 …
case (0, 2): … handle a fixed item in section 0 …
case (1, 0): … handle a fixed item as the first item of section 1 …
case (1, _): … handle a variable number of items following that …
default: fatalError()
}
This wildcard case breaks down as follows:
case (1, _):
^ wildcard-pattern
^ pattern
^ expression-pattern
^ pattern
^^^^^^ tuple-pattern
^^^^^^ pattern
^^^^^^ case-item-list
^^^^^^^^^^^^ case-label
You use the identifier pattern with every if let
statement.
if let message = message {
print(message)
}
The pattern breaks down as follows:
let message = message
^^^^^^^^^ initializer
^^^^^^^ identifier-pattern
^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^^ optional-binding-condition
^^^^^^^^^^^^^^^^^^^^^ condition
^^^^^^^^^^^^^^^^^^^^^ condition-list
Note that this is not a value binding pattern; the let
is part of the optional-binding-condition construct.
The following contains two instances of the value binding pattern.
switch (indexPath.section, indexPath.row) {
case (0, let row): … handle all items in section 0, using `row` as an index …
case let (section, _): … handle all remaining sections, using `section` …
}
The first instance breaks down as follows:
case (0, let row):
^^^ identifier-pattern
^^^ pattern
^^^^^^^ value-binding-pattern
^ expression-pattern
^ pattern
^^^^^^^^^^^^ tuple-pattern
^^^^^^^^^^^^ pattern
^^^^^^^^^^^^ case-item-list
^^^^^^^^^^^^^^^^^^ case-label
And the second:
case let (section, _):
^ wildcard-pattern
^ pattern
^^^^^^^ identifier-pattern
^^^^^^^ pattern
^^^^^^^^^^^^ tuple-pattern
^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^ value-binding-pattern
^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^ case-item-list
^^^^^^^^^^^^^^^^^^^^^^ case-label
This snippet uses a tuple pattern to destructure the tuple coming back from enumerated()
for (offset, element) in ["Hello", "Cruel", "World!"].enumerated() {
…
}
The pattern breaks down as follows:
(offset, element)
^^^^^^^ identifier-pattern
^^^^^^^ pattern
^^^^^^ identifier-pattern
^^^^^^ pattern
^^^^^^^^^^^^^^^^^ tuple-pattern
^^^^^^^^^^^^^^^^^ pattern
Enumeration case patterns most commonly show up in the case of a switch
statement.
let error: Error? = …
switch error {
case .none: … handle the `nil` case …
case .some(let x): … handle the non-`nil` case, with x unwrapped …
}
There are two examples here. The first does not contain the trailing tuple pattern, and it breaks down as follows:
case .none:
^^^^^ enumeration-case-pattern
^^^^^ pattern
^^^^^ case-item-list
^^^^^^^^^^^ case-label
The second enumeration case pattern does contain the trailing tuple pattern, which in turn contains a value binding pattern wrapped around an identifier pattern.
case .some(let x):
^ identifier-pattern
^ pattern
^^^^^ value-binding-pattern
^^^^^ pattern
^^^^^^^ tuple-pattern
^^^^^^^ pattern
^^^^^^^^^^^^ enumeration-case-pattern
^^^^^^^^^^^^ pattern
^^^^^^^^^^^^ case-item-list
^^^^^^^^^^^^^^^^^^ case-label
The optional pattern is a short cut for an enumeration case pattern. You can rewrite the example above as:
let error: Error? = …
switch error {
case nil: … handle the `nil` case …
case let x?: … handle the non-`nil` case, with x unwrapped …
}
Here the pattern in the second case breaks down as follows:
case let x?:
^ identifier-pattern
^ pattern
^^ optional-pattern
^^ pattern
^^^^^^ value-binding-pattern
^^^^^^ pattern
^^^^^^ case-item-list
^^^^^^^^^^^^ case-label
I most commonly use type casting patterns in catch clauses, but here’s an example of something completely different.
class Cell { }
class DIPSwitchCell : Cell { }
class LEDCell : Cell { }
let cell: Cell = … some unspecified subclass …
switch cell {
case is DIPSwitchCell: break // do nothing for a DIP switch
case let led as LEDCell: … flash the LED represented by `led` …
default: fatalError() // crash for any other subclass
}
The pattern in the first case breaks down as follows:
case is DIPSwitchCell:
^^^^^^^^^^^^^ type
^^^^^^^^^^^^^^^^ is-pattern
^^^^^^^^^^^^^^^^ type-casting-pattern
^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^ case-item-list
^^^^^^^^^^^^^^^^^^^^^^ case-label
And the one in the second like so:
case let led as LEDCell:
^^^^^^^ type
^^^ identifier-pattern
^^^ pattern
^^^^^^^^^^^^^^ as-pattern
^^^^^^^^^^^^^^ type-casting-pattern
^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^ value-binding-pattern
^^^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^ case-item-list
^^^^^^^^^^^^^^^^^^^^^^^^ case-label
Speaking of catch clauses, let’s break down some common patterns, starting with this:
catch let error as NSError
^^^^^^^ type
^^^^^ identifier-pattern
^^^^^ pattern
^^^^^^^^^^^^^^^^ as-pattern
^^^^^^^^^^^^^^^^ type-casting-pattern
^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^ value-binding-pattern
^^^^^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch-clause
My next example breaks down in the same way:
catch let error as MyErrorType
^^^^^^^^^^^ type
^^^^^ identifier-pattern
^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^ as-pattern
^^^^^^^^^^^^^^^^^^^^ type-casting-pattern
^^^^^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^^^^^ value-binding-pattern
^^^^^^^^^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch-clause
This example differs by having a where clause:
catch let error as MyErrorType where error.myProperty == 42
^^^^^^^^^^^^^^^^^^^^^^ expression
^^^^^^^^^^^^^^^^^^^^^^ where-expression
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ where-clause
^^^^^^^^^^^ type
^^^^^ identifier-pattern
^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^ as-pattern
^^^^^^^^^^^^^^^^^^^^ type-casting-pattern
^^^^^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^^^^^ value-binding-pattern
^^^^^^^^^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch-clause
Finally, here’s how to catch one specific error:
catch MyErrorType.myErrorCode
^^^^^^^^^^^^^^^^^^^^^^^ enumeration-case-pattern
^^^^^^^^^^^^^^^^^^^^^^^ pattern
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ catch-clause
Here’s some very bad code that uses the expression pattern for frog pluralisation.
switch frogCount {
case 1, -1: print("frog")
case _: print("frogs")
}
The first case breaks down as follows:
case 1, -1:
^ ^^ expression-pattern
^ ^^ pattern
^^^^^ case-item-list
^^^^^^^^^^^ case-label