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: 3Each 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.97Storing 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 7Summary
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.
