Swift - Closures



What is Closures in Swift?

Closures are self-contained blocks of functionalities that can be used inside the program and perform the specified task. Closures are similar to blocks in Objective-C or anonymous functions in other programming languages. They are allowed to capture and store the references to the variables or constants from the context in which they are defined. Also, they can access the values of these constants or variables even if they are outside the original scope.

Closure in the Swift has three forms −

  • Global Functions − They are closures with a name that do not capture any value.

  • Nested Function − A function defined inside another function. They have names and can capture values from the enclosing function.

  • Closure Expression − Using this we can write closure more concisely. We can write unnamed closures that capture values from the adjacent block.

Closures Expression

Closure expression provides a way to write inline closure or inline and unnamed functions. It supports various short, optimized and focused syntaxes to write closure without losing its clarity. So first of all we will see the basic syntax of closure then we will move to the other expression syntaxes supported by Swift −

  • Inferring parameter and return value types from context.

  • Implicit returns from single-expression closures.

  • Shorthand argument names

  • Operator methods

Defining and Calling basic closure

In Swift, we can define a closure simply by using curly braces{}. These curly braces contain the closure parameters, return type(if available), the in keyword to separate the parameter and return type with the body, and the body of the closure. The parameters of closure can be regular, in-out and variadic parameters, but they do not contain default values. Tuples can also used as a parameter and return types in closures. And we can call a closure by passing value for the parameters(if available).

Syntax

Following is a generic syntax to define closure which accepts parameters and returns a data type −

{(parameters) -> return type in
   // body of closure
}

Following is the syntax for calling the closure.

closure(parameters)

Example

Swift program to demonstrate a closure without parameters.

// Creating a Closure
let studname = { print("Welcome to Swift 4 Closures") }

// Calling a closure
studname()
Output

It will produce the following output −

Welcome to Swift 4 Closures

Example

Swift program to demonstrate a closure with parameters.

// Closure with parameters
let divide = {(val1: Int, val2: Int) -> Int in 
   return val1 / val2 
}

// Calling closure
let result = divide(200, 20)
print (result)
Output

It will produce the following output −

10

Inferring type from context

A closure can also pass as an inline closure expression in the function or method, so we are allowed to infer the types of its parameters and the return value. That means we are not explicitly required to write the type of parameter passed in the closure and the type of value returned by the closure, the compiler will automatically infer the type of the closure from the context in which it is used.

Example

Swift program to pass a closure as a parameter in the function.

// Define an array of String 
let myValues = ["Mohina", "Suman", "Mohit"]

// Use the 'map' function to add the given string in all the elements of the array 
/* The type of closure is inferred according to the fact that 'map()' is 
applied to an array of strings. So here the closure adds a specified string to each element
hence the inferred type of the closure is (String) -> String*/
let newArray = myValues.map { $0 + " Hey" }

print(newArray) 
Output

It will produce the following output −

["Mohina Hey", "Suman Hey", "Mohit Hey"]

Implicit Return From Single-Expression Closure

In Closure, a single expression can implicitly return an expression without explicitly using the return keyword. Or we can say that a closure can return an expression without specifying the return type if it contains only one statement. It makes syntax more straightforward and readable.

Example

Swift program to implicitly return expression from single expression closure.

// Single line closure without return type
let add: (Int, Int) -> Int = { a, b in
   a + b
}

let output = add(5, 6)
print("Addition:", output) 
Output

It will produce the following output −

Addition: 11

Shorthand Argument Names

While working with inline closure we are allowed to write the values of the closure's arguments by the names $0, $1, $2, and so on, instead of naming them. It is the shortest way to express arguments in the closer. Where $0 refers to the first parameter, $1 refers to the second parameter, $2 refers to the third parameter and so on.

If we are using these shorthand argument names, then we can remove the closure argument list from the definition section. The compiler will automatically infer the type of the arguments from the expected function type. We can also remove in keyword because the shorthand argument is defined in the expression body.

Example

Swift program to demonstrate shorthand argument names in closure.

// Creating a closure
var shorthand: (String, String) -> String

// Assigning the second parameter and discarding the first parameter
shorthand = { $1 }

// Calling the closure with two arguments will return the second parameter
print(shorthand("100", "200"))
Output

It will produce the following output −

200

Operators Methods

Swift provides an easy way to access the members by just providing operator functions as closures. Or we can say that using closure we can define the behaviour of the operators by overloading them.

Example

Swift program to demonstrate operator method in closure.

// Define a custom operator method for addition numbers 
func + (left: (Double, Double), right: (Double, Double)) -> (Double, Double) {
   return (left.0 + right.0, left.1 + right.1)
}

// Using the custom operator in a closure
let addNumbers: ((Double, Double), (Double, Double)) -> (Double, Double) = { $0 + $1 }

let num1  = (3.0, 3.0)
let num2 = (5.0, 2.0)

// Adding the values using addNumbers closure
let result = addNumbers(num1, num2)
print("Resultant Sum: \(result)")
Output

It will produce the following output −

Resultant Sum: (8.0, 5.0)

Trailing Closures

Trailing closures are the special type of closures in Swift. When a closure is defined outside of the function parentheses() especially when the closure is the last argument of the function then such type of closure is known as trailing closure.

Such types of closures are generally used when the closure is long and it is impossible to write it inline. If the function contains only closure as an argument, then while calling the function or method we can remove parentheses(), for example, names.map{$1 = $0}.

Syntax

Following is the syntax for the trailing function −

// Function that takes closure
func functionName(closure:()->void){
  // Function body
}

// Calling function without trailing closure
functionName(closure:{// closure body})

// Calling function with trailing closure
functionName(){// closure body}

Example

Swift program to demonstrate trailing closure.

// Function to operate on two numbers using a trailing closure
func operation(_ x: Int, _ y: Int, op: (Int, Int) -> Int) -> Int {
   return op(x, y)
}

// Using trailing closure to add two numbers
let res = operation(8, 9) { (a, b) in
   return a + b
}
print("Sum: \(res)")

Output

It will produce the following output −

Sum: 17

Trailing functions are common among high-order functions like map, filter or sort, where the closure works as a callback or transformation.

Example

Swift program to demonstrate trailing closure in high-order function.

// Array of string
let names = ["Mohan", "Mohit", "Roy", "Suman"]

// Calling map() function with trailing function
let lowercaseNames = names.map { $0.lowercased() }

// Displaying the names in lowercased
print(lowercaseNames) 

Output

It will produce the following output −

["mohan", "mohit", "roy", "suman"]

A single function can have multiple trailing closures, where we can remove the argument label of the first trailing closure and label the remaining trailing closures.

Example

Swift program to demonstrate multiple trailing closures in a function.

// Function with multiple trailing closures
func Operations(_ x: Int, _ y: Int, op1: (Int, Int) -> Int, op2: (Int, Int) -> Int) -> (Int, Int) {
   let result1 = op1(x, y)
   let result2 = op2(x, y)
   return (result1, result2)
}

// Using multiple trailing closures
var output = Operations(11, 6, op1: { $0 + $1 }, op2: { $0 * $1 })

print(output)

Output

It will produce the following output −

(17, 66)

Capturing Values and Reference Types

In Swift, capturing constants and variable values is done with the help of closures. It further refers to and modifies the values for those constants and variables inside the closure body even though the original scope in which the variable or constant is defined, is no longer exists.

While assigning a function or closure to a constant or a variable, we are setting that constant or variable as a reference to that function or closure. That means if we assign a closure to two constants or variables, then both the constants or variables refer to the same closure.

Example

func calcDecrement(forDecrement total: Int) -> () -> Int {
   var overallDecrement = 100
   func decrementer() -> Int {
      overallDecrement -= total
      print(overallDecrement)
      return overallDecrement
   }
   return decrementer
}
let decrem = calcDecrement(forDecrement: 18)
print(decrem())
print(decrem())
print(decrem())

Output

It will produce the following output −

82
82
64
64
46
46

Every time the outer function calcDecrement is called it invokes the decrementer() function, decrements the value by 18 and returns the result with the help of outer function calcDecrement. Here calcDecrement acts as a closure.

Even though the function decrementer() does not have any arguments closure by default refers to variables 'overallDecrement' and 'total' by capturing its existing values. The copy of the values for the specified variables is stored with the new decrementer() function. Swift handles memory management functions by allocating and deallocating memory spaces when the variables are not in use.

Advertisements