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 pieceMeaning
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).