Swift Cheatsheet
Functions & Closures
Use this Swift reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Functions
func add(_ a: Int, _ b: Int) -> Int { a + b // single-expression bodies return implicitly } func greet(name: String = "friend", loudly: Bool = false) -> String { let base = "Hello, \(name)" return loudly ? base.uppercased() : base } greet() // "Hello, friend" greet(name: "Ada", loudly: true) func sum(_ values: Int...) -> Int { // variadic, arrives as [Int] values.reduce(0, +) } func minMax(_ xs: [Int]) -> (min: Int, max: Int)? { // tuple return guard let mn = xs.min(), let mx = xs.max() else { return nil } return (mn, mx) }
| Signature piece | Meaning |
|---|---|
func f(to name: T) | External label to, internal name name |
func f(_ name: T) | No label at the call site |
func f(name: T = v) | Default value |
func f(_ xs: T...) | Variadic |
func f(_ x: inout T) | Caller passes &x, mutation visible outside |
@discardableResult func f() | Callers may ignore the return value |
inout
func bump(_ value: inout Int) { value += 1 } var count = 0 bump(&count) // count == 1
Function types and higher-order functions
let op: (Int, Int) -> Int = add op(2, 3) // 5 func apply(_ f: (Int) -> Int, to x: Int) -> Int { f(x) } apply({ $0 * 2 }, to: 21) // 42 func makeCounter() -> () -> Int { // returns a closure var n = 0 return { n += 1 return n } } let next = makeCounter() next() // 1 next() // 2, closures capture n by reference
Closure syntax, from full to shorthand
let nums = [3, 1, 4, 1, 5] nums.sorted(by: { (a: Int, b: Int) -> Bool in return a > b }) nums.sorted(by: { a, b in a > b }) // inferred types, implicit return nums.sorted { $0 > $1 } // trailing closure + shorthand args nums.sorted(by: >) // an operator is already a function let doubled = nums.map { $0 * 2 } let evens = nums.filter { $0.isMultiple(of: 2) } let names = users.map(\.name) // key-path literal as a function
Multiple trailing closures label every closure after the first:
UIView.animate(withDuration: 0.3) { view.alpha = 0 } completion: { _ in view.removeFromSuperview() }
Capture lists
final class Loader { var onDone: ((Data) -> Void)? func start() { fetch { [weak self] data in // avoid a self → closure → self cycle guard let self else { return } self.handle(data) } } func handle(_ data: Data) {} } var i = 0 let snapshot = { [i] in print(i) } // copies i at creation time i = 99 snapshot() // prints 0
[weak self] makes the capture optional and nil after deallocation. [unowned self] skips the optional but crashes if the object is already gone. Capture lists are only needed when a reference type stores a closure that references it back.
@escaping and @autoclosure
var handlers: [() -> Void] = [] func onCommit(_ f: @escaping () -> Void) { // stored for later, so it escapes handlers.append(f) } func log(_ message: @autoclosure () -> String, enabled: Bool) { if enabled { print(message()) } // argument evaluated lazily } log("state: \(expensiveDump())", enabled: false) // expensiveDump() never runs
Closure parameters are non-escaping by default: they must run before the function returns unless marked @escaping (completion handlers, stored callbacks).