Conditional logic
There are three key types of conditional logic in Swift (known as branch statements in the grammar): the if
statement, the switch
statement, and the guard
statement. Unlike other languages, the body of the if
must be surrounded with braces {}
; and if typed in at the interpreter, the {
opening brace must be on the same line as the if
statement. The guard
statement is a specialized if
statement for use with functions and is covered in the section on functions later in this chapter.
If statements
Conditionally unwrapping an optional value is so common that a specific Swift pattern optional binding has been created to avoid evaluating the expression twice:
> var shopping = [ "Milk", "Eggs", "Coffee", "Tea", ] > var costs = [ "Milk":1, "Eggs":2, "Coffee":3, "Tea":4, ] > var cost = 0 > if let cc = costs["Coffee"] { . cost += cc . } > cost $R0: Int = 3
The if
block only executes if the optional value exists. The definition of the cc
constant only exists for the body of the if
block, and it does not exist outside of that scope. Furthermore, cc
is a non-optional type, so it is guaranteed not to be nil
.
Note
Swift 1 only allowed a single let
assignment in an if
block causing a pyramid of nested if
statements. Swift 2 allows multiple comma-separated let
assignments in a single if
statement.
> if let cm = costs["Milk"], let ct = costs["Tea"] { . cost += cm + ct . } > cost $R1: Int = 8
To execute an alternative block if the item cannot be found, an else
block can be used:
> if let cb = costs["Bread"] { . cost += cb . } else { . print("Cannot find any Bread") . } Cannot find any Bread
Other boolean expressions can include the true
and false
literals, and any expression that conforms to the BooleanType
protocol, the ==
and !=
equality operators, the ===
and !==
identity operators, as well as the <
, <=
, >
, and >=
comparison operators. The is type
operator provides a test to see whether an element is of a particular type.
Tip
The difference between the equality operator and the identity operator is relevant for classes or other reference types. The equality operator asks Are these two values equivalent to each other?, whereas the identity operator asks Are these two references equal to each other?
There is a boolean operator that is specific to Swift, which is the ~=
pattern match operator. Despite the name, this isn't anything to do with regular expressions; rather, it's a way of asking whether a pattern matches a particular value. This is used in the implementation of the switch
block, which is covered in the next section.
As well as the if
statement, there is a ternary if expression that is similar to other languages. After a condition, a question mark (?) is used followed by an expression to be used if the condition is true, then a colon (:) followed by the false expression:
> var i = 17 i: Int = 17 > i % 2 == 0 ? "Even" : "Odd" $R0: String = "Odd"
Switch statements
Swift has a switch
statement that is similar to C and Java's switch
. However, it differs in two important ways. Firstly, case
statements no longer have a default fall-through behavior (so there are no bugs introduced by missing a break
statement), and secondly, the value of the case
statements can be expressions instead of values, pattern matching on type and range. At the end of the corresponding case
statement, the evaluation jumps to the end of the switch
block unless the fallthrough
keyword is used. If no case
statements match, the default
statements are executed.
Note
A default
statement is required when the list of cases is not exhaustive. If they are not, the compiler will give an error saying that the list is not exhaustive and that a default
statement is required.
> var position = 21 position: Int = 21 > switch position { . case 1: print("First") . case 2: print("Second") . case 3: print("Third") . case 4...20: print("\(position)th") . case position where (position % 10) == 1: . print("\(position)st") . case let p where (p % 10) == 2: . print("\(p)nd") . case let p where (p % 10) == 3: . print("\(p)rd") . default: print("\(position)th") . } 21st
In the preceding example, the expression prints out First
, Second
, or Third
if the position is 1
, 2
, or 3
, respectively. For numbers between 4
and 20
(inclusive), it prints out the position with a th
ordinal. Otherwise, for numbers that end with 1, it prints st
; for numbers that end with 2, it prints nd
, and for numbers that end with 3, it prints rd
. For all other numbers it prints th
.
The 4...20
range expression in a case
statement represents a pattern. If the value of the expression matches that pattern, then the corresponding statements will be executed:
> 4...10 ~= 4 $R0: Bool = true > 4...10 ~= 21 $R1: Bool = false
There are two range operators in Swift: an inclusive or closed range, and an exclusive or half-open range. The closed range is specified with three dots; so 1...12
will give a list of integers between one and twelve. The half-open range is specified with two dots and a less than operator; so 1..<10
will provide integers from 1 to 9 but excluding 10.
The where
clause in the switch
block allows an arbitrary expression to be evaluated provided that the pattern matches. These are evaluated in order, in the sequence they are in the source file. If a where
clause evaluates to true
, then the corresponding set of statements will be executed.
The let
variable syntax can be used to define a constant that refers to the value in the switch
block. This local constant can be used in the where
clause or the corresponding statements for that specific case. Alternatively, variables can be used from the surrounding scope.
Note
If multiple case
statements need to match the same pattern, they can be separated with commas as an expression list. Alternatively, the fallthrough
keyword can be used to allow the same implementation to be used for multiple case
statements.