Control Flow
Swift’s control flow mechanisms direct program execution through conditionals, loops, and control transfer statements, offering flexibility and expressive constructs.
Conditionals
Conditionals evaluate conditions to execute code selectively.
If-Else Statements
Execute code based on boolean conditions, supporting complex expressions.
Example: Basic If-Else:
let score = 85
if score >= 90 {
print("Grade: A")
} else if score >= 80 {
print("Grade: B")
} else if score >= 70 {
print("Grade: C")
} else {
print("Grade: F")
}
Example: Compound Conditions:
let hasPermission = true
let isAdmin = false
if hasPermission && !isAdmin {
print("Regular user access")
} else if hasPermission && isAdmin {
print("Admin access")
} else {
print("No access")
}
Switch Statements
Match values against patterns, requiring exhaustiveness for enums and supporting complex matching.
Example: Basic Switch:
let grade = "A"
switch grade {
case "A":
print("Excellent")
case "B":
print("Good")
case "C", "D": // Multiple values
print("Passing")
default:
print("Failing")
}
Example: Range Matching:
let temperature = 25
switch temperature {
case ...0:
print("Freezing")
case 1...15:
print("Cold")
case 16...25:
print("Comfortable")
default:
print("Hot")
}
Example: Tuple Matching:
let point = (x: 1, y: 0)
switch point {
case (0, 0):
print("Origin")
case (_, 0):
print("On x-axis")
case (0, _):
print("On y-axis")
case let (x, y):
print("Point (\(x), \(y))")
}
Loops
Loops repeat code execution for iteration.
For-In Loops
Iterate over sequences (ranges, arrays, dictionaries).
Example: Range Iteration:
for i in 1...5 {
print(i) // 1, 2, 3, 4, 5
}
Example: Collection Iteration:
let colors = ["Red", "Blue", "Green"]
for color in colors {
print(color)
}
let scores = ["Alice": 90, "Bob": 85]
for (name, score) in scores {
print("\(name): \(score)")
}
Example: Stride for Custom Steps:
for i in stride(from: 0, to: 10, by: 2) {
print(i) // 0, 2, 4, 6, 8
}
While Loops
Repeat while a condition is true.
Example:
var count = 0
while count < 5 {
print(count)
count += 1
}
Repeat-While Loops
Execute at least once before checking the condition.
Example:
var attempts = 3
repeat {
print("Attempt \(attempts)")
attempts -= 1
} while attempts > 0
Control Transfer Statements
Alter the flow of loops or switches.
- break: Exit the current loop or switch.
- continue: Skip to the next iteration.
- fallthrough: Proceed to the next switch case.
- return: Exit a function.
- throw: Propagate an error.
Break Example:
for i in 1...10 {
if i > 5 {
break
}
print(i) // 1, 2, 3, 4, 5
}
Continue Example:
for i in 1...10 {
if i % 2 == 0 {
continue
}
print(i) // 1, 3, 5, 7, 9
}
Fallthrough Example:
let value = 2
switch value {
case 2:
print("Matched 2")
fallthrough
case 3:
print("Matched 3 or fell through")
default:
break
}
// Output: Matched 2
// Matched 3 or fell through
Labeled Statements
Use labels to control nested loops or switches.
Example:
outerLoop: for i in 1...3 {
innerLoop: for j in 1...3 {
if j == 2 {
continue outerLoop
}
print("i: \(i), j: \(j)")
}
}
// Output: i: 1, j: 1; i: 2, j: 1; i: 3, j: 1
Where Clauses
Filter iterations or patterns with where
.
Example:
for number in 1...10 where number % 2 == 0 {
print("Even: \(number)") // 2, 4, 6, 8, 10
}
switch point {
case let (x, y) where x == y:
print("On diagonal")
default:
print("Elsewhere")
}
Guard Statements
Ensure conditions early in functions, reducing nesting.
Example:
func process(user: String?, age: Int?) {
guard let user = user, let age = age, age >= 18 else {
print("Invalid user data")
return
}
print("Processing \(user), age \(age)")
}
processDelegate(user: "Alice", age: 20) // "Processing Alice, age 20"
processDelegate(user: nil, age: 20) // "Invalid user data"
Best Practices
- Switch Exhaustiveness: Use
default
or cover all cases for enums. - Guard for Early Exits: Reduce nesting in functions.
- Avoid Deep Nesting: Use labeled statements or refactor complex logic.
- Where Clauses: Simplify filtering in loops and switches.
- Clear Conditions: Use descriptive variables in conditionals.
- Fallthrough Sparingly: Only when behavior is intentional and documented.
Troubleshooting
- Non-Exhaustive Switch: Add missing cases or
default
. - Infinite Loops: Ensure loop conditions eventually fail.
- Unexpected Break: Check label targets in nested loops.
- Complex Patterns: Simplify switch cases with helper functions.
- Guard Misuse: Ensure
guard
exits (e.g.,return
,throw
).
Example: Comprehensive Control Flow
enum TaskStatus {
case pending
case inProgress(percentage: Int)
case completed
case failed(reason: String)
}
func processTasks(_ tasks: [TaskStatus]) {
taskLoop: for (index, task) in tasks.enumerated() {
switch task {
case .pending:
print("Task \(index\}: Pending")
continue taskLoop
case .inProgress(let percentage) where percentage >= 90:
print("Task \(index\}: Almost done (\(percentage)%)")
case .inProgress(let percentage)
print("Task \(index\): Working (\(percentage)%)")
case .completed:
print("Task \(index\): Done")
case .failed(let reason)):
print("Task \(index\): Failed - \(reason)")
break taskLoop
}
}
}
let tasks = [
TaskStatus.pending,
TaskStatus.inProgress(percentage: 50),
TaskStatus.inProgress(percentage: 95),
TaskStatus.completed,
TaskStatus.failed(reason: "Timeout")
]
processTasks(tasks)
// Output:
// Task 0: Pending
// Task 1: Working (50%)
// Task 2: Almost done (95%)
// Task 3: Done