Swift Closures

A closure is a block of code that you can store in a variable, pass to a function, or return from a function. Think of it as a function without a name. Closures are everywhere in Swift — you use them with arrays, timers, networking, and animations.

The Errand Note Analogy


┌───────────────────────────────────────────────────┐
│  Handing someone a note with instructions:        │
│                                                   │
│  "When the package arrives,                       │
│   sign for it and put it in the kitchen."         │
│                                                   │
│  You give someone a task to run later.            │
│  A closure is that exact note — instructions      │
│  saved to run at the right moment.                │
└───────────────────────────────────────────────────┘

Function vs Closure Comparison

// Named function
func double(n: Int) -> Int {
    return n * 2
}

// Same logic as a closure
let double = { (n: Int) -> Int in
    return n * 2
}

print(double(5))   // 10

Both do the same job. The closure stores the logic in a variable instead of giving it a name with func.

Closure Syntax Breakdown


┌───────────────────────────────────────────────────┐
│  { (parameter: Type) -> ReturnType in             │
│        code here                                  │
│  }                                                │
│                                                   │
│  ① {}  → wraps the closure body                   │
│  ② ()  → parameter list                           │
│  ③ ->  → return type                              │
│  ④ in  → separates signature from body            │
└───────────────────────────────────────────────────┘

Passing a Closure to a Function

func runTask(action: () -> Void) {
    print("Starting task...")
    action()
    print("Task done.")
}

runTask(action: {
    print("Doing the work!")
})
// Starting task...
// Doing the work!
// Task done.

The function runTask accepts a closure as a parameter. The closure runs inside the function when action() is called.

Trailing Closure Syntax

runTask {
    print("Doing the work!")
}

When a closure is the last argument, Swift lets you move it outside the parentheses. This is called a trailing closure and makes the code read more naturally.

Closures with Arrays – map, filter, sorted

map – Transform Every Element

let prices = [100, 200, 300]
let discounted = prices.map { price in price / 2 }
print(discounted)   // [50, 100, 150]

filter – Keep Matching Elements

let scores = [45, 78, 92, 55, 88]
let passing = scores.filter { $0 >= 60 }
print(passing)   // [78, 92, 88]

sorted – Reorder Elements

let names = ["Zara", "Amit", "Priya"]
let sorted = names.sorted { $0 < $1 }
print(sorted)   // ["Amit", "Priya", "Zara"]

The $0 and $1 are shorthand for the first and second closure parameters. Swift fills them in automatically.

Capturing Values

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

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

The closure captures the count variable from its surrounding context. Even after makeCounter finishes, the closure remembers and updates count. This is called capturing.

Escaping Closures

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

func schedule(action: @escaping () -> Void) {
    pendingActions.append(action)
}

schedule { print("Task 1 runs later.") }
pendingActions[0]()   // Task 1 runs later.

An @escaping closure lives beyond the function call — it is stored and used later. Without @escaping, Swift assumes the closure finishes before the function returns.

Leave a Comment

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