Swift Closures

A closure is a block of code you can store in a variable, pass into a function, or return from a function. Think of a closure like a recipe card you hand to someone: you write the steps once and they can follow it later, whenever they need to. Closures are the foundation of Swift's higher-order functions and are used everywhere in iOS development.

Basic Closure Syntax

let greet = { (name: String) -> String in
    return "Hello, \(name)!"
}

print(greet("Alice"))   // Output: Hello, Alice!

The structure is: { (parameters) -> ReturnType in body }. The keyword in separates the signature from the body.

Diagram: Closure Anatomy

{ (name: String) -> String in return "Hello, \(name)!" }
  |____________|   |______|    |_______________________|
   parameters     return type         body

Closures as Function Parameters

Passing a closure into a function is the most common use. The function calls your closure at the right time.

func performAction(action: () -> Void) {
    print("Getting ready...")
    action()
    print("Done.")
}

performAction(action: {
    print("Firing the rocket!")
})
// Output:
// Getting ready...
// Firing the rocket!
// Done.

Trailing Closure Syntax

When a closure is the last argument to a function, you can write it outside the parentheses. This keeps call sites clean and readable.

performAction {
    print("Deploying parachute!")
}
// Output:
// Getting ready...
// Deploying parachute!
// Done.

Shorthand Argument Names

Inside closures, Swift provides automatic argument names $0, $1, $2 etc., so you can skip writing parameter names.

let numbers = [5, 2, 8, 1, 9, 3]

let sorted = numbers.sorted { $0 < $1 }
print(sorted)   // Output: [1, 2, 3, 5, 8, 9]

let doubled = numbers.map { $0 * 2 }
print(doubled)  // Output: [10, 4, 16, 2, 18, 6]

Diagram: Closure Shorthand Evolution

Full form:
numbers.sorted(by: { (a: Int, b: Int) -> Bool in return a < b })

Type inferred:
numbers.sorted(by: { a, b in return a < b })

Implicit return:
numbers.sorted(by: { a, b in a < b })

Shorthand arguments:
numbers.sorted(by: { $0 < $1 })

Trailing + operator:
numbers.sorted(by: <)

Capturing Values

Closures can capture and store values from the surrounding context. Even after the original variable is gone, the closure holds onto its own copy.

func makeCounter() -> () -> Int {
    var count = 0
    let counter = {
        count += 1
        return count
    }
    return counter
}

let counter = makeCounter()
print(counter())   // Output: 1
print(counter())   // Output: 2
print(counter())   // Output: 3

Each call to counter() increments the same captured count. The closure remembers the variable across calls.

@escaping Closures

By default, a closure passed to a function runs while that function is active. An @escaping closure runs after the function returns — common in network calls and async tasks.

var completionHandlers: [() -> Void] = []

func registerTask(action: @escaping () -> Void) {
    completionHandlers.append(action)
}

registerTask {
    print("Task completed!")
}

completionHandlers.first?()
// Output: Task completed!

@autoclosure

An @autoclosure wraps an expression in a closure automatically. The caller writes plain code; the function receives a closure it can call later.

func logIfTrue(_ condition: @autoclosure () -> Bool) {
    if condition() {
        print("Condition is true.")
    }
}

logIfTrue(2 + 2 == 4)
// Output: Condition is true.

Closures With map, filter, reduce

let prices = [9.99, 4.49, 19.99, 2.50, 14.00]

// filter: keep items under $10
let cheap = prices.filter { $0 < 10 }
print(cheap)   // Output: [9.99, 4.49, 2.5]

// map: apply 10% discount
let discounted = prices.map { $0 * 0.9 }
print(discounted)

// reduce: total cost
let total = prices.reduce(0) { $0 + $1 }
print("Total: \(total)")   // Output: Total: 50.97

Storing Closures in Variables

var operations: [(Int, Int) -> Int] = []

operations.append { $0 + $1 }
operations.append { $0 * $1 }
operations.append { $0 - $1 }

for op in operations {
    print(op(10, 3))
}
// Output: 13  30  7

Summary

Closures are self-contained blocks of code you can pass around and call later. Swift shorthand makes them concise: use $0, $1 for arguments and trailing closure syntax for readability. Closures capture surrounding values, making them powerful for callbacks, event handling, and higher-order functions like map, filter, and reduce.

Leave a Comment

Your email address will not be published. Required fields are marked *