Closures
Closures are blocks of functionality that are self-contained, and can be passed around. They are similar to blocks in C and Objective-C, as well as lambdas in other languages. Functions are a special type of closures.
- Closure expression syntax
- Closure inline
- Closure as a variable
- Closure as a function
- Function with a closure parameter
- Typealias for a closure parameter
- Function with an
@escaping
closure parameter - Closure that captures variables
- Further reading
- Notes
Closure expression syntax
Closures can be defined with closure expression syntax, which has the general form:
{ (parameters) -> return type in statements }
Closure inline
Using closure expression syntax:
let sortedInts = [4, 30, 7, 9, 1].sorted(by: { (x: Int, y: Int) -> Bool in
return x < y
})
Because this closure is the last and only argument,1 trailing closure syntax may be used and the parentheses are optional:
let sortedInts = [4, 30, 7, 9, 1].sorted { (x: Int, y: Int) -> Bool in
return x < y
}
When the argument and/or return types are known, the type annotations are optional:
let sortedInts = [4, 30, 7, 9, 1].sorted { x, y in return x < y }
Also, closure parameters can be referenced by position instead of by name:
let sortedInts = [4, 30, 7, 9, 1].sorted { $0 < $1 }
Closure as a variable
A closure can be stored as a variable and used later. Using closure expression syntax:
let myClosure = { (x: Int, y: Int) -> Bool in
return x < y
}
let sortedInts = [4, 30, 7, 9, 1].sorted(by: myClosure)
Closure as a function
A function is a type of closure, so a closure can be stored as a function to be used later.
func myClosure(x: Int, y: Int) -> Bool {
return x < y
}
let sortedInts = [4, 30, 7, 9, 1].sorted(by: myClosure)
Function with a closure parameter
A function can be created with a closure parameter:
func multiply(x: Int, y: Int, completion: (Int) -> Void) {
completion(x * y)
}
multiply(x: 5, y: 6) { print($0) } // Output: 30
A closure parameter may be optional:
func multiply(x: Int, y: Int, completion: ((Int) -> Void)?) {
completion?(x * y)
}
multiply(x: 5, y: 6) { print($0) } // Output: 30
It may have labeled arguments for readability:2
func multiply(x: Int, y: Int, completion: (_ result: Int) -> Void) {
completion(x * y)
}
multiply(x: 5, y: 6) { print($0) } // Output: 30
Or multiple arguments:
func multiply(x: Int, y: Int, completion: (Int, Error?) -> Void) {
completion(x * y, nil)
}
multiply(x: 5, y: 6) { print($1 ?? $0) } // Output: 30
Or no arguments:
func multiply(x: Int, y: Int, completion: () -> Void) {
completion()
}
multiply(x: 5, y: 6) { print("Hello") } // Output: Hello
Typealias for a closure parameter
To improve readability and reduce duplicate code, a closure typealias may be used:
typealias MultiplyCompletion = (_ result: Int, _ error: Error?) -> Void
func multiply(x: Int, y: Int, completion: MultiplyCompletion) {
completion(x * y, nil)
}
multiply(x: 5, y: 6) { print($1 ?? $0) } // Output: 30
Function with an @escaping
closure parameter
An escaping closure can be called even after the function has returned:3
func multiplyRemotely(x: Int, y: Int, completion: @escaping (Int) -> Void) {
APIManager.shared.multiply(x: x, y: y, completion: completion)
}
multiplyRemotely(x: 5, y: 6) { print($0) }
// Output: 30
Closure that captures variables
A closure can capture a variable. This closure captures greeting
:
var greeting = "Hello"
let greet = { print(greeting) }
greet() // Hello
greeting = "Bonjour"
greet() // Bonjour
A capture list, such as [greeting]
, can be used to capture a copy of a variable:
var greeting = "Hello"
let greet = { [greeting] in print(greeting) }
greet() // Hello
greeting = "Bonjour"
greet() // Hello
A closure can have both a capture list and parameters:
var greeting = "Hello"
let greet = { [greeting] (name: String) in print("\(greeting), \(name)") }
greet("Isabella") // Hello, Isabella
Further reading
Notes
-
When a closure is the last parameter of a function, it is called a trailing closure.Β ↩
-
It is commonly asked why the argument names donβt appear in the closure call, like
completion(result: x * y)
. The reason for this is that as of Swift 3, closure argument labels are no longer part of the closure type.Β ↩ -
This makes it possible to make a network call to a remote server, return the function, then have the closure get executed when the server response is received. The benefit of this is that the rest of the execution of the function, and subsequently parent functions, do not have to get blocked while the app waits for a response.Β ↩