Closures

Introduction

Closures are self-contained, anonymous blocks of functionality that can be stored in constants and variables or included as parameters of functions. They can be passed around and executed in the code of your program. Unlike regular functions, discussed in the previous lesson, closures do not have a name that describes what the closure does.

In this lesson, we learn how to define closures and how to use them with constants, variables, and functions.

Closure syntax

{(parameters) -> returnType in
statements
}

A closure is anonymous and has no name to describe its functionality. A closure is enclosed in curly braces ({}) and the in keyword is placed before the closure body.

Closures assigned to constants or variables

A closure without parameters or return value

I need to explain what you find between the curly braces ({}) of the closure:

  • () : empty parameter list: no parameters are defined;
  • -> Void : the keyword Void indicates that the closure does not return a value;
  • in : keyword to end the parameter and return definition and start the closure body;
  • print(“Welcome to my new app”) : the closure contains one statement.

The closure is assigned to a constant named welcomeMessage. The closure can be executed with welcomeMessage().

A closure with parameters and without return value

The previous screenshot shows a single String parameter named name. Void indicates that no return value is available. The closure is assigned to the constant named welcomeMessage.

A closure with parameter(s) and return value(s)

The previous screenshot shows a single Double parameter named celsius, and the closure returns a Double value. The closure is assigned to a constant named cToF.

Shorthand notation

You will find that the definition and use of a closure are very easy to understand. However, if you use the full syntax (parameters, return type, in keyword, statements) of a closure, its definition can sometimes seem very complicated. Fortunately, Swift also offers a shorter notation.

In many cases, Swift can infer the type of the parameters and the type of the return value from the context. Because all constant literals in the previous screenshot are of type Double, Swift infers that the parameter celsius will also be of type Double, and likewise for the return value. If Swift can actually derive it, there is no point for us to define it as well. In this case, we can omit the parameter list and the type of the return value. And even using the keyword in makes no sense anymore.

For single-expression closures, Swift assumes that an implicit return statement follows. Therefore, the keyword return can also be omitted.

And if you don’t like typing and want to avoid typing long (but understandable) parameter names, you can replace them with $0 to refer to the first argument, $1 to the second argument, and so on.

Let’s use these rules to rewrite our previous closure:

The previous screenshot shows the shorthand notation (no parameter list, no return data type, no keyword in, no return statement, and a shorthand parameter name) for the closure to convert degrees Celsius to degrees Fahrenheit.

And now it is no longer difficult to define another one:

A closure as a parameter of a function

The function priceCalculation() has three parameters: price: Double, vat: Double and a closure totalPrice with two Double parameters and a Double return value. The function priceCalculation() prints the result (the return value) of the closure totalPrice. The functionality of the closure must be defined when calling the function priceCalculation().

When calling the function priceCalculation() the argument for price is 200.0, the argument for vat is 42.0 and we define the closure in full detail (parameters, return value, in keyword and the calculation in the return statement).

When calling the priceCalculation() function, the price argument is 200.0, the vat argument is 42.0, and we fully define the closure (two parameters, return value type, the keyword in, and the calculation (price + vat) in the return statement).

Using the shorthand notation, we can even write the closure as follows:

Because this closure is the last argument of a function, we can also use the trailing closure syntax. When using the trailing closure syntax, the closure is written after the closing parentheses of the function:

Nested functions are closures

A nested function is a function defined in the body of another function. A nested function can access the parameters and variables defined by the outer function. Nested functions are completely hidden inside the outer function. They cannot be called from other parts of your program.

The functions toKelvin() and toFahrenheit() are encapsulated in the function celsiusConvertor() . As a result, both functions are only accessible from the outer celsiusConvertor() function.

The function celsiusConvertor() has a parameter celsius: Double . This parameter is also available for the inner functions without needing to be defined as a parameter of the internal functions: both functions capture this value from the outer function. Other constants and variables defined in the outer function are also accessible from the inner functions.

The inner functions are accessible as follows:

Because the constant result can be nil, we used the ternary conditional operator to check the value of the result constant.

sorted(by:), map(_:) and filter(_:)

The methods sorted(by:), map(_:) and filter(_:) use a closure to define their functionality.

sorted(by:)

The sorted(by:) method uses a closure comparing two arguments and returning a Bool value. If the first argument (t1) must come before the second argument (t2) in the sorting order, then the comparison t1 > t2 must be true (descending order). However, if the second element (t2) must come before the first element (t1), then the comparison (t1 < t2) must be true (ascending order). The sorted(by:) method returns a new collection (Array, Set, Dictionary…):

map(_:)

The map(_:) method uses a closure to iterate through all elements in a collection. The closure can be used to convert or format each received element. The map(_:) method returns a new collection (Array, Set, Dictionary…):

filter(_:)

The filter(_:) method uses a closure to determine which elements are included in the resulting Array: