Skip to content

Swift Control Flow Statements

In this post, we’re diving into the world of Swift control flow statements. From simple if statements to the more complex guard and, we’ll explore these essential tools with playful and practical examples.

Swift Control Flow Statements Essentials

Control flow statements in Swift guide the execution of your code. Let’s break them down with examples you can try in a Swift playground.

if and if-else Statements Statements

Used for basic conditional operations.

let age = 18
if age >= 18 {
    print("You are an adult.")
} else {
    print("You are a minor.")
}

if as an Expression:

let category = if age >= 18 {
    "Adult"
} else {
    "Minor"
}
print(category)  // Output would be "Adult"

Switch Statement

Swift’s switch statement doesn’t require a break statement in each case as it does in many other languages. In Swift, the switch case automatically exits after the case is executed, unless you explicitly tell it to fall through to the next case.

let fruit = "Apple"

switch fruit {
case "Apple":
    print("It's an apple.")
case "Banana":
    print("It's a banana.")
default:
    print("It's some other fruit.")
}
  • In this example, switch checks the value of fruit.
  • When it matches “Apple”, it executes the first print statement and then exits the switch statement.
  • There’s no need for a break after each case.

switch as an Expression

let color = switch fruit {
    case "Apple":
        "Red"
    case "Banana":
        "Yellow"
    default:
        "Unknown"
}
print(color)  // Output would be "Red"

for-in Loop

Perfect for iterating over collections like arrays or ranges.

for number in 1...5 {
    print("Number is \(number)")
}

while Loop

Runs a block of code as long as a condition is true.

var count = 5
while count > 0 {
    print(count)
    count -= 1
}

repeat-while Loop Statement

Like a while loop, but the condition is at the end.

repeat {
    print("This will run at least once.")
} while false

guard Statement

Used for early exits in a function.

func greet(name: String?) {
    guard let name = name else {
        print("No name provided")
        return
    }
    print("Hello, \(name)!")
}
greet(name: "Alice")
  • The guard statement is used for optional binding and early exit. It checks if the optional name contains a value.
  • let name = name attempts to unwrap the optional name. If name is not nil, it assigns the unwrapped value to a new constant (also conveniently named name but could be named differently), which is available in the remaining part of the function’s scope.
  • The else block executes if the optional name is nil. Inside this block, the function prints “No name provided” and then uses return to exit early. This means the function does not proceed to the greeting if no name is provided.
  • If the optional contains a value, the function skips the else block and continues.

do-catch Block statement

Handles errors in a block of code.

enum VendingMachineError: Error {
    case outOfStock
}

func vend(itemNamed name: String) throws {
    throw VendingMachineError.outOfStock
}

do {
    try vend(itemNamed: "Candy Bar")
} catch {
    print("Item not available.")
}
  • enum VendingMachineError: Error declares a new enumeration type named VendingMachineError that conforms to the Error protocol. This is a way of defining custom error types in Swift.
  • case outOfStock is a case of the VendingMachineError enum, representing a specific kind of error where an item is out of stock in the vending machine.
  • func vend(itemNamed name: String) throws defines a function named vend that takes a String parameter and is marked with throws. This indicates that the function can throw an error.
  • throw VendingMachineError.outOfStock inside the function, an error is thrown using the throw keyword. This error is the outOfStock case of the VendingMachineError enum. When this line is executed, the function immediately stops executing further code and propagates the error to be handled by its caller.
  • The do block is used to handle errors thrown by functions marked with throws.
  • try vend(itemNamed: "Candy Bar") calls the vend function and is prefixed with try, indicating that this function call might throw an error. The function is called with the argument "Candy Bar".
  • If the vend function throws an error (in this case, it always does), the execution immediately moves to the catch block.
  • The catch block is where you handle the error. Here, it simply prints the message "Item not available.". This is a generic catch block that catches any error thrown within the do block.

break and return statements

break exits a loop, and return exits a function.

Imagine a function that searches through an array of numbers. If it finds the number 3, it uses break to stop the search. If the number 5 is found before number 3, the function immediately returns, indicating the search was unsuccessful.

func searchForNumber(in numbers: [Int]) {
    for number in numbers {
        if number == 3 {
            print("Found number 3, stopping search.")
            break  // Stop the loop if number 3 is found
        } else if number == 5 {
            print("Encountered number 5 before finding 3, exiting function.")
            return  // Exit the function if number 5 is found before 3
        }
        print("Checking number \(number)")
    }
    print("Search completed.")
}

searchForNumber(in: [1, 2, 5, 3, 4])  // Test the function with an array

  • The function searchForNumber takes an array of integers as its input.
  • It iterates over each number in the array using a for-in loop.
  • During each iteration:
    • If the current number is 3, it prints a message and executes break. This stops the loop, and the search ends successfully.
    • If the current number is 5, it prints a different message and executes return. This causes the function to exit immediately, even if there are more numbers to check. In this case, the search ends without finding 3.
    • For any other number, it simply prints that number is being checked.
  • After the loop, a final message “Search completed.” is printed, but this will only be reached if neither 3 nor 5 is found in the array.

throw statement

Throws an error for error handling.

func checkTemperature(_ temp: Int) throws {
    if temp > 100 {
        throw VendingMachineError.outOfStock
    }
}

defer statement

Runs code just before exiting a scope

func processFile() {
    defer {
        print("Closing file.")
    }
    print("Processing file.")
}
processFile()
  • The defer block is used to schedule a block of code that runs just before the function exits.
  • The code inside the defer block (print("Closing file.")) is executed regardless of how the function exits. It could be a normal exit at the end of the function or an exit caused by an error or a return statement elsewhere in the function.
  • When processFile is called, the first thing it does is set up the defer block. However, the code inside the defer block is not executed immediately. It’s merely scheduled to run later.
  • The function then prints “Processing file.”
  • As the function reaches its end, the defer block is executed, printing “Closing file.”

#if, #endif, #available statements

Used for compile-time checks and platform/version-specific code.

#if os(macOS)
    print("Running on macOS.")
#else
    print("Not running on macOS.")
#endif

if #available(iOS 14, *) {
    print("iOS 14 or newer")
} else {
    print("Older iOS version")
}

Conclusion

Swift’s control flow statements are like the conductor of an orchestra, guiding the flow of your program’s execution with precision and flexibility. Experiment with these examples in a Swift playground, and watch as your coding skills grow!

 

 

Back To Top