Closures

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.

Closure syntax

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.

Capturing values

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset