Statements

Statements control the flow of execution. Swift has three broad categories — simple statements (expressions and declarations), control-flow statements (loops and branches), and compiler-control statements. This reference summarizes each, including defer, do, and availability conditions.

Loop statements

Swift has three loops:

for n in 1...3 where n.isMultiple(of: 1) { print(n) }
while hasMore { advance() }
repeat { tick() } while running

Branch statements

if chooses between branches based on Boolean conditions or optional/case bindings. guard requires a condition to hold and otherwise transfers control out of the current scope; its bindings stay in scope after the statement. switch matches a value against patterns and must be exhaustive, with no implicit fallthrough.

As of Swift 5.9+, if and switch can also be used as expressions that produce a value.

guard let url = URL(string: text) else { return }

let label = switch score {
case 90...: "A"
case 80..<90: "B"
default: "?"
}

Switch cases support interval matching, tuple matching, value bindings, where clauses, and compound cases separated by commas.

Labeled statements

A loop or branch statement can carry a label, letting break and continue target a specific outer statement rather than the innermost one.

search: for row in grid {
    for cell in row {
        if cell == target { break search }
    }
}

Control transfer statements

StatementEffect
breakEnd the enclosing (or labeled) loop or switch
continueSkip to the next loop iteration
fallthroughContinue into the next switch case
returnExit the current function, optionally with a value
throwThrow an error to be handled by a caller

defer and do

A defer block runs when the current scope exits, by any path, in reverse order of declaration — ideal for cleanup. A do statement introduces a new scope and is the home of error handling via catch clauses; a bare do is also useful just to scope locals.

func read() throws {
    let handle = open()
    defer { handle.close() }   // always runs on exit
    do {
        try handle.parse()
    } catch let e as ParseError {
        print(e)
    }
}

Availability condition

An availability condition, #available, guards code that uses APIs introduced in a newer OS; its inverse, #unavailable, guards the older-platform path. Use them in if, guard, and while conditions.

if #available(iOS 17, *) {
    useNewAPI()
} else {
    useFallback()
}

Compiler control statements

Compiler control statements are not runtime control flow — they direct compilation. They include conditional compilation (#if / #elseif / #else / #endif), line-control (#sourceLocation), and diagnostic directives (#warning("…"), #error("…")).

#if os(iOS)
import UIKit
#elseif os(macOS)
import AppKit
#endif

#warning("TODO: handle empty state")

Summary

Swift's statements give you exhaustive, fallthrough-free branching, three loop forms, labeled control transfer, deterministic cleanup with defer, structured error handling with do/catch, runtime API gating with #available, and compile-time configuration with #if directives.