Closures are functions without the func
keyword. Closures are self-contained blocks of code that provide a specific functionality and can be stored, passed around, and used in the code like functions. Closures capture the constant and variables of the context in which they are defined. Although closures are equivalent of blocks in Objective-C, they have a simpler syntax in Swift compared to the C and Objective-C block syntax. Nested functions, which we have covered in a previous section, are special cases of closures. Closures are reference types that can be stored as variables, constants, and type aliases. They can be passed to and returned from functions.
A general closure syntax is as follows:
{ (parameters) -> ReturnType in // body of closure }
A closure definition starts with {
, then we define the closure type, and finally we use the in
keyword to separate the closure definition from its implementation.
After the in
keyword, we write the body of the closure and finish our closure by closing }
.
Closures can be used to define variables. The following closure defines a variable of a type closure that accepts Int
and returns Int
:
let closureName: (Int) -> (Int) = {/* */ }
Closures can be stored as optional variables. The following closure defines a variable of a type closure that accepts Int
and returns Optional Int
:
var closureName: (Int) -> (Int)?
Closures can be defined as typealiases
. The following example presents typealias
of a closure that has two Int
parameters and returnsInt
:
typealias closureType = (Int, Int) -> (Int)
The same typealias
could be used for a function type definition as functions are named closures in Swift.
Closures can be used as an argument to a function call. For instance, the following example presents a function that is called with a closure that receives Int
and returns Int
:
func aFunc(closure: (Int) -> Int) -> Int { // Statements, for example: return closure(5) } let result = aFunc(closure: { number in // Statements, for example: return number * 3 }) print(result)
Closures can be used as function parameters. The following example shows an array sort method that receives a closure:
var anArray = [1, 2, 5, 3, 6, 4] anArray.sort(isOrderedBefore: { (param1: Int, param2: Int) -> Bool in return param1 < param2 })
This syntax can be simplified with implied types as the Swift compiler has the ability to infer the types for parameters from the context:
anArray.sort(isOrderedBefore: { (param1, param2) -> Bool in return param1 < param2 })
The syntax can be further simplified with implied return types using the Swift type inference:
anArray.sort(isOrderedBefore: { (param1, param2) in return param1 < param2 })
Swift enables us to eliminate the open and close parentheses if we need to pass the closure as the last parameter of a function, in other words, if our closure is a trailing closure:
anArray.sort { (param1, param2) in return param1 < param2 }
Also, Swift provides a shorthand argument notation that can be used instead of using arguments:
anArray.sort { return $0 < $1 }
We can simplify this syntax even further by eliminating the return
keyword as we have only one line of expression as follows:
anArray.sort { 0 < $1 }
Using the Swift type inference, we were able to simplify the closure syntax drastically.
Closures can capture variables and constants from the surrounding context in which they are created. Closures can refer to these variables and modify them within their body, even if the original scope that defined variables no longer exists.
A closure is said to escape a function when the closure is passed as an argument to the function but is called after the function returns. One way that a closure can escape is by being stored in a variable that is defined outside the function.
The following is an example of escaping closures, in other words, completion handlers:
func sendRequest(responseType: String.Type, completion: (responseData:String, error:NSError?) -> Void) { // execute some time consuming operation, if successful { completion(responseData: "Response", error: nil) //} } sendRequest(String.self) { (response: String?, error: NSError?) in if let result = response { print(result) } else if let serverError = error { // Error } }
We have a function named sendRequest
that have two parameters—responseType
of the String.Type
type and completion
which is type of closure that takes a String
, and an optional NSError
parameters and does not return any value.
Suppose that we execute some asynchronous time-consuming operations in the body of the function, such as reading from a file, reading from a database, or calling a web service.
To call this function, we provide String.self
and a closure as arguments. Our closure has two variables in it—a variable named response
of the Optional String
type and an error variable of the NSError
optional type. As our function does not have any return type, it does not return any value to its caller. Here comes the concept of escaping a function.
Our passed closure escapes our function as it will be called after our time-consuming asynchronous operation finishes with success and the following call happens:
completion(responseData: "Response", error: nil)
In this call, we pass the responseData
and error and call back the completion closure. Then the body of closure in the caller function is executed with passed variables. This concept is a very powerful concept that eases all asynchronous operations. It is very readable and easy to follow compared with mechanisms such as delegation and notification.