Control Flow, Functions, and Closures
Branching and loops
if conditions must be Bool; Swift has no truthiness. guard exits the current scope early, and its else must transfer control (return, throw, break, continue, or a never-returning call). switch must be exhaustive, does not fall through implicitly, and supports ranges, tuples, value bindings, and where clauses. Use fallthrough deliberately; it ignores the next case's condition. Labeled statements disambiguate nested break and continue.
Functions
Parameters have an external argument label and an internal name: func move(from start: Point, to end: Point). The first label defaults to the parameter name; use _ to omit it. Default values, variadic parameters (Int...), and inout parameters (called with &) change call-site syntax. A function returning a tuple can name its elements.
Closures
Closures capture surrounding constants and variables by reference. Non-escaping closures, the default for function parameters, are guaranteed to finish before the function returns, so self need not be written. Annotate stored closures with @escaping; inside a class, refer to self explicitly to make the capture visible and avoid accidental strong cycles. Trailing-closure syntax and shorthand $0 reduce ceremony.
Traps
- A
guardelseblock that only prints and continues. - Assuming
switchfalls through like C. - Confusing argument labels with parameter names.
- Forgetting
@escapingwhen storing a closure, or capturingselfstrongly in a long-lived closure.
Rule of thumb
Prefer guard for preconditions and keep the happy path unindented. When a closure outlives the call, decide ownership explicitly: [weak self] or [unowned self], then unwrap safely.