Control Flow by Example - Swift Programming Language

  • In Swift, there are three kinds of statements: simple statements, compiler control statements, and control flow statements.
    • Simple statements are the most common and consist of either an expression or a declaration.
    • Compiler control statements allow the program to change aspects of the compiler’s behavior and include a conditional compilation block and a line control statement.
    • Control flow statements are used to control the flow of execution in a program.
  • There are several types of control flow statements in Swift, including loop statements, branch statements, and control transfer statements.
  • Loop statements allow a block of code to be executed repeatedly, branch statements allow a certain block of code to be executed only when certain conditions are met, and control transfer statements provide a way to alter the order in which code is executed.
  • In addition, Swift provides a do statement to introduce scope, and catch and handle errors, and a defer statement for running cleanup actions just before the current scope exits.

1. Loop Statements

  • Loop statements allow a block of code to be executed repeatedly, depending on the conditions specified in the loop.
  • Swift has three loop statements: a for-in statement, a while statement, and a repeat-while statement.
  • Control flow in a loop statement can be changed by a break statement and a continue statement.

1.1 For-In Statement

  • A for-in statement allows a block of code to be executed once for each item in a collection (or any type) that conforms to the Sequence protocol.

  • A for-in statement has the following form:

for item in collection {
statements
}
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}

/* prints
Hello, Anna!
Hello, Alex!
Hello, Brian!
Hello, Jack! */
  • You can also iterate over a dictionary to access its key-value pairs.
  • Each item in the dictionary is returned as a (key, value) tuple when the dictionary is iterated, and you can decompose the (key, value) tuple’s members as explicitly named constants for use within the body of the for-in loop.
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCountYou can also use `for-in` loops with numeric ranges.) legs")
}

/* prints:
spiders have 8 legs
ants have 6 legs
cats have 4 legs */
  • You can also use for-in loops with numeric ranges.
for index in 1...5 {
    print("\(index) * 5 is \(index * 5)")
}

/* prints: 
1 * 5
2 * 10
3 * 15
4 * 20
5 * 25 */
  • If you don’t need each value from a sequence, you can ignore the values by using an underscore _ in place of a variable name.
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}
print("\(base) ^ \(power)ss = \(answer)")

/* results:
3 ^ 10 = 59049 */
  • stride(from:to:by:) - Returns the sequence of values where last is the last value in the progression that is less than end.
  • stride(from:to:by:) counts from the start point up to by excluding the to parameter.
for i in stride(from: 0, to: 10, by: 1) {
    print(i) 
}

/* prints:
0
1
2
3
4
5
6
7
8
9 */
  • stride(from:through:by:) - counts from the start point up to by including the through parameter.
for i in stride(from: 0, through: 10, by: 1) {
    print(i) 
}
/* prints:
0
1
2
3
4
5
6
7
8
9
10 */

1.2. While Loops

  • A while loop performs a set of statements until a condition becomes false.

  • These kinds of loops are best used when the number of iterations is not known before the first iteration begins.

  • Swift provides two kinds of while loops:

    • while evaluates its condition at the start of each pass through the loop.
    while condition {
        statements
    }
    
    • repeat-while evaluates its condition at the end of each pass through the loop.
    repeat {
        statements
    } while condition
    

1.2.1 while

  • A while statement is executed as follows:
    • Step1: The condition is evaluated.
      If true, execution continues to step 2. If false, the program is finished executing the while statement.
      * Step2: The program executes the statements, and execution returns to step 1.
var i:Int = 0
while i < 10 {
    print(i)
    i += 1 
}

/* prints:
0
1
2
3
4
5
6
7
8
9 */
var title:String = ""
while title != "aaaaa" {
    title = title + "a"  
    print(title)
}

/* prints: 
a
aa
aaa
aaaa
aaaaa */

1.2.2 repeat-while

  • A repeat-while statement is executed as follows:
    • Step1: The program executes the statements, and execution continues to step 2.
    • Step2: The condition is evaluated.
      If true, execution returns to step 1. If false, the program is finished executing the repeat-while statement.
var j: Int = 10
repeat {
   print(j)
}
while(j < 10)

/* prints:
10 */

2. Branch Statements

  • Branch statements allow the program to execute certain parts of code depending on the value of one or more conditions.
  • The values of the conditions specified in a branch statement control how the program branches and, therefore, what block of code is executed.
  • Swift has three branch statements:
    • an if statement
    • a guard statement
    • a switch statement.

2.1 if-else

  • An if statement is used for executing code based on the evaluation of one or more conditions.

  • There are two basic forms of an if statement. In each form, the opening and closing braces are required.

    • The first form allows code to be executed only when a condition is true and has the following form:
     if condition {
         statements
     }
    
    • The second form of an if statement provides an additional else clause and is used for executing one part of code when the condition is true and another part of code when the same condition is false. When a single else clause is present, an if statement has the following form:
    if condition {
        statements to execute if condition is true
    } else {
        statements to execute if condition is false
    }
    
  • if-else example:

var numArray = [10, 20, 30, 40, 50, 60, 70]
if(numArray.contains(20)){
    print("true it contains 20")
}else{
    print("number is not there")
}

/* prints: 
true it contains 20 */

2.2 Guard

  • A guard statement is used to transfer program control out of a scope if one or more conditions aren’t met.
  • A guard statement has the following form:
guard condition else {
    statements
}
  • The value of any condition in a guard statement must be of type Bool or a type bridged to Bool.
  • The condition can also be an optional binding declaration.
let optionalNumArray: [Int?]
optionalNumArray = [1, 2, nil, 4, 5]

for num in optionalNumArray{
    guard let num = num else{
        print(":)")
        continue
    }
    print(num)
}

/* prints:
1
2
:)
4
5 */
  • The else clause of a guard statement is required, and must either call a function with the Never return type or transfer program control outside the guard statement’s enclosing scope using one of the following statements:

    • return
    • break
    • continue
    • throw

2.3 Switch

  • A switch statement allows certain blocks of code to be executed depending on the value of a control expression.
  • A switch statement provides an alternative to the if statement for responding to multiple potential states.
  • A switch statement considers a value and compares it against several possible matching patterns. It then executes an appropriate block of code, based on the first pattern that matches successfully.
switch some value to consider {
case value 1:
    respond to value 1
case value 2,
     value 3:
    respond to value 2 or 3
default:
    otherwise, do something else
}
var grade = 45

switch grade {
    
case 90 ..< 100:
    print("A")
case (80 ..< 90):
    print("B")
case (70 ..< 80):
    print("C")
case (0 ..< 70):
    print("D")
    
default:
    print("F. You failed")//Any number less than 0 or greater than 99
    
}

//prints: D

2.3.1 Interval Matching in switch

  • Values in switch cases can be checked for their inclusion in an interval.
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hundreds of"
default:
    naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")

// prints: There are dozens of moons orbiting Saturn.

2.3.2 Tuples in switch

  • You can use tuples to test multiple values in the same switch statement.
  • Each element of the tuple can be tested against a different value or interval of values. Alternatively, use the underscore character (_), also known as the wildcard pattern, to match any possible value.
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("\(somePoint) is at the origin")
case (_, 0):
    print("\(somePoint) is on the x-axis")
case (0, _):
    print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
    print("\(somePoint) is inside the box")
default:
    print("\(somePoint) is outside of the box")
}

/* prints 
(1, 1) is inside the box */

2.3.3. Value Bindings in switch

  • A switch case can name the value or values it matches to temporary constants or variables, for use in the body of the case.
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("on the x-axis with an x value of \(x)")
case (0, let y):
    print("on the y-axis with a y value of \(y)")
case let (x, y):
    print("somewhere else at (\(x), \(y))")
}
/* prints: 
on the x-axis with an x value of 2 */

2.3.4 Where in switch

  • A switch case can use a where clause to check for additional conditions.
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}

/* prints: 
(1, -1) is on the line x == -y */

2.3.5 Compound Cases in switch

  • Multiple switch cases that share the same body can be combined by writing several patterns after case, with a comma between each of the patterns.
  • If any of the patterns match, then the case is considered to match.
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
     "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter) is a consonant")
default:
    print("\(someCharacter) is not a vowel or a consonant")
}

/* prints:
e is a vowel */

3. Control Transfer Statements

  • Control transfer statements change the order in which your code is executed, by transferring control from one piece of code to another.
  • Swift has five control transfer statements:
    • continue
    • break
    • fallthrough
    • return
    • throw

3.1 Continue

  • The continue statement tells a loop to stop what it is doing and start again at the beginning of the next iteration through the loop.
let numbersArray = [20, 30, 40, 50, 60, 70, 80, 90, 10]
for num in numbersArray{
    if(num > 10){
        continue
    }
    print(num)
}

// prints: 10

3.2 Break

  • The break statement ends execution of an entire control flow statement immediately.
  • The break statement can be used inside a switch or loop statement when you want to terminate the execution of the switch or loop statement earlier than would otherwise be the case.

if

let numbersArray = [20, 30, 40, 50, 60, 70, 80, 90, 10]
for num in numbersArray{
    if num > 30{
        break
    }
    print(num)
}

/* prints:
20
30 */

switch

for num in numbersArray{
    switch num {
    case 10:
        print(num)
    case 20:
        print(num)
    case 30:
        break
    default:
        print("nothing here")
    }
}

/* prints:
20
nothing here
nothing here
nothing here
nothing here
nothing here
nothing here
10 */

3.3 Fallthrough

  • switch statements don’t fallthrough the bottom of each case and into the next one. That is, the entire switch statement completes its execution as soon as the first matching case is completed.
for num in numbersArray{
    switch num {
    case 10:
        print(num)
    case 20:
        print(num)
    case 30:
        print(num)
        fallthrough
    case 40:
        print(num)
    default:
        print("nothing here")
    }
}

/* prints:
20
30
30
40
nothing here
nothing here
nothing here
nothing here
nothing here
10 */

3.4 return

  • Generally in swift we use return statement in functions or methods to return the values based on our requirements.
  • By using return keyword we can return values from functions / methods based on our requirements in swift programming language.

return expression

func printA(a: String) -> String{
    return a
}
print(printA(a: "abc"))

// prints: abc

return

func myFunc() -> Int {
    let myNumber = 16 % 3
    if myNumber == 0 {
        return 0
    }
    else if myNumber == 1 {
        return 1
    }
    return 0
}

// prints: 1

3.5 throw

enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}
struct Item {
    var price: Int
    var count: Int
}

class VendingMachine {
    var inventory = [
        "Candy Bar": Item(price: 12, count: 7),
        "Chips": Item(price: 10, count: 4),
        "Pretzels": Item(price: 7, count: 11)
    ]
    var coinsDeposited = 0
    
    func vend(itemNamed name: String) throws {
        guard let item = inventory[name] else {
            throw VendingMachineError.invalidSelection
        }
        
        guard item.count > 0 else {
            throw VendingMachineError.outOfStock
        }
        
        guard item.price <= coinsDeposited else {
            throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
        }
        
        coinsDeposited -= item.price
        
        var newItem = item
        newItem.count -= 1
        inventory[name] = newItem
        
        print("Dispensing \(name)")
    }
}
let favoriteSnacks = [
    "Alice": "Chips",
    "Bob": "Licorice",
    "Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
    let snackName = favoriteSnacks[person] ?? "Candy Bar"
    try vendingMachine.vend(itemNamed: snackName)
}
struct PurchasedSnack {
    let name: String
    init(name: String, vendingMachine: VendingMachine) throws {
        try vendingMachine.vend(itemNamed: name)
        self.name = name
    }
}
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.invalidSelection {
    print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
    print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
}

// prints: Insufficient funds. Please insert an additional 2 coins.

You can download the swift playground of all above examples from Here



You've successfully subscribed to Developer Insider
Great! Next, complete checkout for full access to Developer Insider
Welcome back! You've successfully signed in
Success! Your account is fully activated, you now have access to all content.