Skip to content

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:

swift
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:

swift
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:

swift
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:

swift
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:

swift
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:

swift
for i in 1...5 {
    print(i) // 1, 2, 3, 4, 5
}

Example: Collection Iteration:

swift
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:

swift
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:

swift
var count = 0
while count < 5 {
    print(count)
    count += 1
}

Repeat-While Loops

Execute at least once before checking the condition.

Example:

swift
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:

swift
for i in 1...10 {
    if i > 5 {
        break
    }
    print(i) // 1, 2, 3, 4, 5
}

Continue Example:

swift
for i in 1...10 {
    if i % 2 == 0 {
        continue
    }
    print(i) // 1, 3, 5, 7, 9
}

Fallthrough Example:

swift
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:

swift
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:

swift
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:

swift
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

swift
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

Released under the MIT License.