Swift Closures
Closures are self-contained blocks of functionality that can be passed around and executed later, and they can capture variables from the scope where they were created.
What Is a Closure?
A closure is very similar to a function, except it has no name and can be written inline wherever an expression is expected. In fact, Swift's named functions are simply closures with a name attached. A closure's type looks like (ParameterTypes) -> ReturnType, and you can store one in a constant or variable just like any other value, then call it later using the same parentheses syntax you'd use to call a function.
A basic closure
let greet: (String) -> String = { name in
return "Hello, \(name)!"
}
print(greet("Priya")) // Hello, Priya!Capturing Values from the Surrounding Scope
A closure can 'capture' constants and variables from the context in which it's defined, keeping a reference to them even after that context has finished executing. This is what allows a function that returns a closure to hand back a piece of state along with the behavior that uses it. Each call to the outer function creates a fresh, independent capture, so two closures produced by the same factory function don't share state with each other.
Capturing state in a counter
func makeCounter() -> () -> Int {
var count = 0
return {
count += 1
return count
}
}
let counter = makeCounter()
print(counter()) // 1
print(counter()) // 2
print(counter()) // 3Trailing Closure Syntax
When a function's last parameter is a closure, Swift lets you move that closure outside the parentheses entirely — this is trailing closure syntax, and it's used constantly with functions like sorted(by:), map, and filter. Inside the closure, Swift can often infer the parameter and return types from context, letting you drop them, and for very short closures you can skip naming parameters altogether and use the automatically provided shorthand names $0, $1, and so on.
Trailing closures and shorthand arguments
let scores = [42, 7, 88, 15, 63]
let descending = scores.sorted { $0 > $1 }
print(descending) // [88, 63, 42, 15, 7]
let names = ["Sam", "Zoe", "Ann"]
let sortedNames = names.sorted { first, second in
first < second
}
print(sortedNames) // ["Ann", "Sam", "Zoe"]- $0, $1, ... refer to closure parameters by position, no names needed
- Drop return for a single-expression closure body — it's implied
- Parameter and return types can usually be inferred from context
- Parentheses around the argument list can be omitted entirely for a trailing closure
Exercise: Swift Closures
If a closure captures a `var` from its surrounding scope, and that variable changes afterward, what does the closure see?