Closures in Swift
Closures are self-contained blocks of functionality that can be passed around and used in your code. You can think of them as anonymous functions โ functions without a name. Closures are everywhere in Swift, from sorting arrays to handling button taps.
// A simple closure assigned to a variable
let greet = { (name: String) -> String in
return "Hello, \(name)!"
}
print(greet("Alice")) // Hello, Alice!
Try it Yourself ->
Closure Syntax
The full closure syntax looks like this: { (parameters) -> ReturnType in code }. The in keyword separates the parameter/return type declaration from the body of the closure. As you get more comfortable, you'll learn to simplify this syntax considerably.
let multiply = { (a: Int, b: Int) -> Int in
return a * b
}
let product = multiply(4, 5)
print(product) // 20
Try it Yourself ->
Trailing Closure Syntax
When the last argument to a function is a closure, you can write it as a trailing closure โ placing it outside the function parentheses. This makes your code read more naturally, especially with long closure bodies.
let numbers = [5, 3, 1, 4, 2]
// Using trailing closure syntax
let sorted = numbers.sorted { a, b in
return a < b
}
print(sorted) // [1, 2, 3, 4, 5]
Try it Yourself ->
Shorthand Argument Names
Swift provides shorthand names for closure arguments โ $0 refers to the first argument, $1 to the second, and so on. You can drop the parameter list and the in keyword entirely, making closures incredibly concise.
let numbers = [5, 3, 1, 4, 2]
// Using shorthand argument names
let sorted = numbers.sorted { $0 < $1 }
print(sorted) // [1, 2, 3, 4, 5]
// Even shorter with operator function
let sorted2 = numbers.sorted(by: <)
print(sorted2) // [1, 2, 3, 4, 5]
Try it Yourself ->
Capturing Variables
Closures can capture and store references to variables from the surrounding scope. This means the closure keeps those variables alive even after the original scope has finished executing. This is powerful for creating stateful closures.
func makeCounter() -> () -> Int {
var count = 0
return {
count += 1
return count
}
}
let counter = makeCounter()
print(counter()) // 1
print(counter()) // 2
print(counter()) // 3
Try it Yourself ->
Closures in Swift APIs
You'll use closures constantly in Swift. Array methods like map, filter, and reduce all take closures as arguments. They're also used for completion handlers, sorting, and responding to events. Mastering closures is essential for writing idiomatic Swift.
let names = ["Alice", "Bob", "Charlie", "Diana"]
// Filter names longer than 3 characters
let longNames = names.filter { $0.count > 3 }
print(longNames) // ["Alice", "Charlie", "Diana"]
// Transform names to uppercase
let uppercased = names.map { $0.uppercased() }
print(uppercased) // ["ALICE", "BOB", "CHARLIE", "DIANA"]
Try it Yourself ->