Callbacks and closures

Callbacks, as mentioned, allow us to handle asynchronous operations by specifying what should happen when that operation is completed. Simply put, a callback is any piece of executable code that is passed to a function that will call it at some later point, either synchronously or asynchronously. As an example of synchronous callback, in Chapter 3Diving into Foundation and the Standard Library we used the forEach method available on Array objects:

// Using forEach with a closure:
(1...5).forEach { value in
print("\(value)")
}

This could be equivalently rewritten as follows, where we replace the closure with a function:

// The above is equivalente to this:
func printValue<T>(val : T) { print("\(val)") }
(1...5).forEach(printValue)

These two examples should help clarify that callbacks and closures correspond to different concepts, although with some overlap, since closures can be used to implement callbacks.

Closures, also known as lambdas in functional languages, and as blocks in C and Objective C, are a very powerful mechanism used to define an anonymous function that carries over a portion of the context where it is defined. In technical terms, a closure implements lexically scoped name binding, which means they bind entities belonging to their lexical scope to local identifiers. In less technical terms, a closure can be considered as a function, plus an environment that maps the free variables used in the function to values or references that were bound to the same identifiers in the context where the closure was created. Behaviorally, a closure closes over the variables or constants it refers from the context where it was defined.

Actually, in Swift, global functions and nested functions are special cases of closures. The most poignant form of closure, though, are closure expressions. Those are unnamed functions that can be written using a lightweight syntax that aims to foster a clean, clear, and clutter-free style. Swift strives to make writing closure expressions as easy as it can be, by supporting the following features:

(1...5).forEach { value in
print("\(value)")
}

As you can see, there was no need to specify the type of the closure value parameter, since Swift is able to infer it correctly from the type of the array to which forEach is applied. In general, it is always possible to infer the types of a closure's parameters and return type, but it may be good practice to specify them to improve readability and reduce ambiguity when their purpose is not as straightforward as in the present example. In particular, the Swift compiler will require that you always specify closure complex return types.

// No need for return statement in single-expression closures:
let squares1 = (1...5).map { value in
value * value
}

As you can see, the return keyword has been omitted. Yet, the Swift compiler is able to handle the return value in a sensible way.

// Even better:
let squares2 = (1...5).map { $0 * $0 }

Shorthand argument names can be used in any closure expression.

func forEach(_ body: (Element) throws -> Void) rethrows

Now, Swift allows you to replace the last parameter of the function type with a trailing closure after the function call's parentheses. This provides a more natural syntax that makes the closure appear like a block, but it is still just another argument to the function.