Swift Control Flow — Conditionals, Loops, and Pattern Matching
In this tutorial, you will learn about Swift Control Flow. We cover key concepts, practical examples, and best practices to help you master this topic.
Swift control flow provides powerful constructs for directing program execution, including if-else conditionals, switch statements with pattern matching and value binding, for-in loops with ranges and collections, while and repeat-while loops, guard statements for early exit, and control transfer with break, continue, and fallthrough. This tutorial covers each construct with runnable examples and explains Swift's unique features like interval matching, where clauses, and binding in switch cases.
What You'll Learn
- Using if, else if, and else for conditional branching
- Writing switch statements with pattern matching and value binding
- Using for-in loops with ranges, collections, and stride
- Implementing while and repeat-while loops
- Using guard statements for early exit and optional unwrapping
- Controlling flow with break, continue, and fallthrough
- Applying where clauses for additional conditions
Why It Matters
Control flow is how you Express logic in code. Swift's switch statement is far more powerful than in C-like languages, supporting pattern matching, interval matching, tuple destructuring, and value binding. The guard statement enforces the early-return pattern that makes code more readable by reducing nesting. Mastering these constructs lets you write clearer and safer code.
Real-World Use
The input validation system in Doda Browser uses guard statements at the start of every function to validate parameters, switch statements with pattern matching to handle different URL scheme types, and for-in loops with where clauses to filter through browser tabs efficiently.
Learning Path
flowchart LR A[Collections] --> B[Control Flow\nYou are here] B --> C[Functions] style B fill:#f90,color:#fff
If-Else Conditions
Swift's if statement supports optional binding without forced unwrapping:
import Foundation
let score = 85
let hasPassed = true
if score >= 90 {
print("Grade: A")
} else if score >= 80 {
print("Grade: B")
} else if score >= 70 {
print("Grade: C")
} else {
print("Grade: F")
}
// Multiple conditions with logical operators
if score >= 80 && hasPassed {
print("Excellent work!")
}
// Optional binding in if
let optionalValue: Int? = 42
if let value = optionalValue {
print("Value exists: \(value)")
} else {
print("Value is nil")
}
Output:
Grade: B
Excellent work!
Value exists: 42
Switch Statements
Swift's switch is powerful and exhaustive:
import Foundation
let number = 5
// Basic switch
switch number {
case 1:
print("One")
case 2, 3, 4:
print("Small")
case 5...10:
print("Medium")
case let x where x > 10:
print("Large: \(x)")
default:
print("Unknown")
}
// Interval matching
let temperature = 72
switch temperature {
case ..<32:
print("Freezing")
case 32..<50:
print("Cold")
case 50..<70:
print("Cool")
case 70..<85:
print("Warm")
case 85...:
print("Hot")
default:
print("Unknown")
}
// Tuple pattern matching
let point = (x: 3, y: 3)
switch point {
case (0, 0):
print("Origin")
case (_, 0):
print("On X axis")
case (0, _):
print("On Y axis")
case (-3...3, -3...3):
print("Within 3x3 box")
case let (x, y) where x == y:
print("On diagonal: (\(x), \(y))")
default:
print("Outside: (\(point.x), \(point.y))")
}
Output:
Medium
Warm
Within 3x3 box
For-In Loops
Iterate over ranges, collections, and custom sequences:
import Foundation
// Range iteration
for i in 1...5 {
print(i, terminator: " ")
}
print()
// Stride iteration
print("Every 2 steps:")
for i in stride(from: 0, to: 10, by: 2) {
print(i, terminator: " ")
}
print()
// Collection iteration
let fruits = ["Apple", "Banana", "Cherry"]
for fruit in fruits {
print("I like \(fruit)")
}
// Enumerated iteration
for (index, fruit) in fruits.enumerated() {
print("\(index + 1). \(fruit)")
}
// Where clause
let numbers = 1...20
for number in numbers where number % 3 == 0 {
print("\(number) is divisible by 3")
}
Output:
1 2 3 4 5
Every 2 steps:
0 2 4 6 8
I like Apple
I like Banana
I like Cherry
1. Apple
2. Banana
3. Cherry
3 is divisible by 3
6 is divisible by 3
9 is divisible by 3
12 is divisible by 3
15 is divisible by 3
18 is divisible by 3
While and Repeat-While
Use while for condition-based looping:
import Foundation
// While loop
var countdown = 5
while countdown > 0 {
print(countdown, terminator: " ")
countdown -= 1
}
print("Go!")
// Repeat-while (guaranteed at least one iteration)
var attempts = 3
var success = false
repeat {
print("Attempt \(4 - attempts)...")
attempts -= 1
// Simulate random success
success = Int.random(in: 1...10) > 7
} while attempts > 0 && !success
if success {
print("Operation succeeded!")
} else {
print("Operation failed after 3 attempts")
}
Output:
5 4 3 2 1 Go!
Attempt 1...
Attempt 2...
Attempt 3...
Operation succeeded!
Guard Statements
Guard provides early exit with the else clause:
import Foundation
func processUser(name: String?, age: Int?, email: String?) {
guard let name = name, !name.isEmpty else {
print("Error: Name is required")
return
}
guard let age = age, age >= 18 else {
print("Error: User must be at least 18 years old")
return
}
guard let email = email, email.contains("@") else {
print("Error: Valid email is required")
return
}
// All validations passed - process the user
print("Processing user: \(name), age \(age), email \(email)")
// Guard also works with boolean conditions
guard age < 120 else {
print("Warning: Age seems unrealistic")
return
}
print("User \(name) registered successfully")
}
processUser(name: "Alice", age: 25, email: "alice@example.com")
processUser(name: nil, age: 20, email: "test@test.com")
processUser(name: "Bob", age: 16, email: "bob@test.com")
Output:
Processing user: Alice, age 25, email alice@example.com
User Alice registered successfully
Error: Name is required
Error: User must be at least 18 years old
Control Transfer
Use break, continue, fallthrough, and return:
import Foundation
// Break in a loop
print("Finding first number > 5:")
for number in 1...10 {
if number > 5 {
print("Found: \(number)")
break
}
}
// Continue in a loop
print("Odd numbers:")
for number in 1...10 {
if number % 2 == 0 {
continue
}
print(number, terminator: " ")
}
print()
// Fallthrough in switch (rarely used)
let value = 2
var result = ""
switch value {
case 1:
result += "One "
fallthrough
case 2:
result += "Two "
fallthrough
case 3:
result += "Three"
default:
break
}
print("Fallthrough result: \(result)")
Output:
Finding first number > 5:
Found: 6
Odd numbers:
1 3 5 7 9
Fallthrough result: Two Three
Common Mistakes
- Missing default in switch: Swift switch must be exhaustive over all possible values. Add a default case for non-enum types.
- Using if-else chains when switch is clearer: For three or more conditions, a switch statement is usually more readable and more powerful.
- Not using guard for early returns: Deeply nested if-let chains are harder to read. Use guard to unwrap and exit early.
- Infinite loops with while: Ensure the loop condition changes within the body. Swift does not protect against infinite loops.
- Forgetting that switch does not implicitly fallthrough: After a case matches, execution exits the switch. Use fallthrough explicitly if needed.
Practice Questions
What is the difference between break and continue?
- break exits the loop entirely. continue skips the current iteration and proceeds to the next one.
How do you write a switch case that matches a range?
- Use interval syntax:
case 1...5:matches values 1 through 5 inclusive.
- Use interval syntax:
What is the purpose of the guard statement?
- guard checks a condition and exits the current scope if it fails. It is used for early returns, input validation, and optional unwrapping.
How does a where clause work in a for-in loop?
for x in collection where condition { }skips elements that do not satisfy the condition, similar to filter.
Challenge: Write a FizzBuzz program that prints numbers 1 to 100, replacing multiples of 3 with "Fizz", 5 with "Buzz", and both with "FizzBuzz". Use a for-in loop with a switch statement inside.
Mini Project
Build an interactive guessing game:
- Generate a random number between 1 and 100.
- Use a repeat-while loop to keep asking the user for guesses.
- Use switch to categorize guesses as "too high", "too low", or "correct".
- Use guard to validate input (must be a number between 1 and 100).
- Track the number of attempts and print a summary when the game ends.
- Use break to exit when the user types "quit".
FAQ
What's Next
Learn how to define and call functions in the Functions tutorial.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro