Swift Closure

Advantages of Closures in Swift

Swift Closure Syntax

Let’s recall the syntax of Functions in swift first.

func name(parameters) -> (return type) {
   //body of function
}

Functions require a func keyword followed by name of function, arguments and return type after -> 

The syntax for Closures is given below.

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

Closures are defined within curly braces. The in keyword separates the arguments and return type from the body. Like functions, closures can also be referenced as types.

Declaring Swift Closures as variables

var myClosure: (ParameterTypes) -> ReturnType

Let’s dive into the playground in XCode and start coding. Let’s create a basic function and a closure that’ll print “Hello World”.

import Foundation

func helloWorldFunc() {
    print("Hello World")
}

var helloWorldClosure = { () -> () in
    print("Hello World")
}

helloWorldFunc() //prints Hello World
helloWorldClosure() //prints Hello World

In the above code, we’ve defined a closure with no parameters and no return type and set it to a variable helloWorldClosure. Calling the closure is similar to calling a function as shown above.

Tip 1: If the closure does not return any value or the type can be inferred you can omit the arrow (->) and the return type. We can shorten the above code as shown below.

var helloWorldClosure = { () in 
   print("Hello World") 
}

Tip 2: If the closure doesn’t require any parameter or can be inferred, remove the argument parenthesis. Now we don’t need the in keyword too. This will further shorten the code as shown below.

var helloWorldClosure = { 
   print("Hello World") 
}

We’ve seen one use case now where closures made the code look less verbose compared to a function. Let’s create a closure with parameters and see how it fares when compared to closures.

Swift Closures with arguments and return type

Let’s create a function and closure that accepts two strings, appends them and return.

import Foundation

func appendString(_ a: String, with b: String) -> String {
    
    return a + " : " + b
}

print(appendString("Swift", with: "Functions")) //print "Swift : Functions\n"

So far so good. Let’s do the same using a closure.

import Foundation

var appendStringClosure = { (a: String, b: String) -> (String) in
    
    return a + " : " + b
}
print(appendStringClosure("Swift", "Closures")) //prints "Swift : Closures\n"

Thanks to type inference as we’d seen earlier, the above closure definition is the same as the following.

import Foundation

var appendStringClosure = { (a: String, b: String) in
    
    return a + " : " + b  // return type inferred
}

another version can be like this

import Foundation

var appendStringClosure: (String, String) -> String = { (a,b) in
    
    return a + " : " + b //Closure type declared on the variable itself
}

We can create a closure without return keyword also

import Foundation

var appendStringClosure: (String, String) -> String  = { (a,b) in

    a + " : " + b   // omit return keyword
}

There’s even a shortened version for the above. Swift allows us to refer to arguments passed to closure using shorthand argument names: $0, $1, $2, etc. for the first, second, third, etc. parameters respectively.

import Foundation

var appendStringClosure: (String, String) -> String  = {
    
    $0 + " : " + $1
}
print(appendStringClosure("Swift", "Closures")) //prints "Swift : Closures"

Let’s take it one more step ahead, add as many arguments as you can.

import Foundation

var appendStringClosure  = { $0 + " : " + $1 + " " + $2 }
print(appendStringClosure("Swift", "Closures", "Awesome")) //prints "Swift : Closures Awesome"

Closures inside Functions

Let’s create a function that takes a function/closure as a parameter.

import Foundation

func operationsSq(a: Int, b:Int, myFunction: (Int, Int) -> Int) -> Int {
    
    return myFunction(a,b)
}
// This function expects a parameter of type (Int, Int)->Int as the third argument.
// Lets pass a function in there as shown below

func addSquareFunc(_ a: Int, _ b:Int) -> Int {
    
     return a*a + b*b
}

print(operationsSq(a:2,b:2, myFunction: addSquareFunc)) //a^2 + b^2 prints 8

Fair enough. But creating a different function for another operation (let’s say subtracting squares etc) would keep increasing the boilerplate code. This is where closure comes to our rescue. We’ve defined the following 4 types of expressions that we’ll be passing one by one in the operationsSq function.

import Foundation

func operationsSq(a: Int, b:Int, myFunction: (Int, Int) -> Int) -> Int {
    
    return myFunction(a,b)
}

var addSq: (Int, Int) -> Int = { $0*$0 + $1*$1 }

var subtractSq: (Int, Int) -> Int = { $0*$0 - $1*$1 }

var multiplySq: (Int, Int) -> Int = { $0*$0 * $1*$1 }

var divideSq: (Int, Int) -> Int = { ($0*$0) / ($1*$1) }

var remainderSq: (Int, Int) -> Int = { ($0*$0) % ($1*$1) }

print(operationsSq(a:2, b:2, myFunction: addSq)) //prints 8
print(operationsSq(a:4, b:5, myFunction: subtractSq)) //prints -9
print(operationsSq(a:5, b:5, myFunction: multiplySq)) //prints 625
print(operationsSq(a:10, b:5, myFunction: divideSq)) //prints 4
print(operationsSq(a:7, b:5, myFunction: remainderSq)) //prints 24

Much shorter than what we achieved with a function as a parameter. In fact, we can directly pass the closure expressions as shown below and it’ll achieve the same result:

import Foundation

func operationsSq(a: Int, b:Int, myFunction: (Int, Int) -> Int) -> Int {
    
    return myFunction(a,b)
}

operationsSq(a:2,b:2, myFunction: { $0 * $0 + $1 * $1 })
operationsSq(a:4,b:5, myFunction: { $0 * $0 - $1 * $1 })
operationsSq(a:5,b:5, myFunction: { $0 * $0 * $1 * $1 })
operationsSq(a:10,b:5, myFunction: { ($0 * $0) / ($1 * $1) })
operationsSq(a:7,b:5, myFunction: { ($0 * $0) % ($1 * $1) })

Trailing Closures

Let’s look at another example where we’ll be adding the sum of exponentials of numbers using closure expressions.

import Foundation

func sumOfExponentials(from: Int, to: Int, myClosure: (Int) -> Int) -> Int {
    var sum = 0
    for i in from...to {
        sum = sum + myClosure(i)
    }
    
    return sum
}

//Trailing closures
print(sumOfExponentials(from:0, to:5) { $0 }) //sum of numbers prints 15
print(sumOfExponentials(from:0, to:5) { $0*$0 }) //sum of squares prints 55
print(sumOfExponentials(from:0, to:5) { $0*$0*$0 }) //sum of cubes prints 225

Convert a numbers array to strings array

Another use case of trailing closure is given below.

import Foundation

var numbers = [1,2,3,4,5,6]
print(numbers)

var strings = numbers.map { "\($0)" }
print(strings) //prints ["1", "2", "3", "4", "5", "6"]

map is a high order function for transforming an array.

Sort numbers in descending order using trailing closures

import Foundation

var randomNumbers = [5,4,10,45,76,11,0,9]

randomNumbers = randomNumbers.sorted{ $0 > $1 }
print(randomNumbers) //[76, 45, 11, 10, 9, 5, 4, 0]

Capture Lists in Closures

By default, closures can capture and store references to any constants and variables from the context in which they are defined (Hence the name closures). Capturing references to variables can cause our closures to behave differently than it’s supposed to. Let’s see this through an example.

import Foundation

var x = 0
var myClosure = { print("The value of x at start is \(x)")  }
myClosure() //prints 0

So far so good. The above example looks pretty straightforward until we do this:

import Foundation

var x = 0
var myClosure = { print("The value of x at start is \(x)")  }
myClosure() //The value of x at start is 0
x = 5
myClosure() //The value of x at start is 5

Now myClosure was defined and initialized before changing the value of x to 5. Why does it print 5 in the end then?

The reason is that closure captures the reference (memory address) of x. Any changes made to value at that memory address would be displayed by the closure when it’s invoked. To make x behave like a value type instead we need to make use of Capture Lists. Capture Lists is an array [] that holds local copies of the variables. Thereby capturing the variables by value types and NOT reference types.

The array with the local variables is displayed before the in keyword as shown below.

import Foundation

var x = 0
var myClosure = { [x] in
    print("The value of x at start is \(x)")
}
myClosure() // The value of x at start is 0
x = 5
myClosure() //The value of x at start is 0

Capturing reference types can be destructive when used in Classes since the closure can hold a strong reference to an instance variable and cause memory leaks. Let’s see an example of this.

import Foundation

class Person {
    
    var x: Int
    
    var myClosure: () -> () = {
        print("Hey there")
    }
    init(x: Int) {
        self.x = x
    }
    
    func initClosure() {
        myClosure = {
            print("Initial value is not defined yet")
        }
    }
    
    deinit {
        print("\(self) Escaped")
    }
}

var a: Person? = Person(x: 0)
a?.initClosure()
a?.x = 5
a?.myClosure()
a = nil

Deinitializers are called automatically, just before instance deallocation takes place. In the above code, it’ll be called when a is set to nil and self doesn’t have any references attached to it. The above code would print:

[quote font=”verdana”] Initial value is not defined yet __lldb_expr_55.Person Escaped [/quote]

import Foundation

class Person {
    
    var x: Int
    
    var myClosure: () -> () = { print("Hey there") }
    
    init(x: Int) {
        self.x = x
    }
    
    func initClosure() {
        myClosure = {
            print("Intial value is \(self.x)")
        }
    }
    
    deinit {
        print("\(self) escaped")
    }
}

var a: Person? = Person(x: 0)
a?.initClosure()
a?.x = 5
a?.myClosure()
a = nil

Whoops! the print statement within deinit is not printed. The closure holds a strong reference to self. This will eventually lead to memory leaks. A remedy for this is to place a weak or unknown reference to self in a capture list inside the closure as seen below.

The code for capture list with self-reference is given below:

import Foundation

class Person {
    
    var x: Int
    
    var myClosure: () -> () = { print("Hey there") }
    
    init(x: Int) {
        self.x = x
    }
    
    func initClosure() {
        myClosure = { [weak self] in
            guard let weakSelf = self else {
                
                return
            }
            print("Intial value is \(weakSelf.x)")}
    }
    
    deinit {
        print("\(self) escaped")
    }
}

var a: Person? = Person(x: 0)
a?.initClosure()
a?.x = 5
a?.myClosure()
a = nil

No memory leaks with the above code!

Escaping Closures

There are two other types of closures too:

import Foundation

var completionHandlers: [() -> Void] = []

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

class SomeClass {
    
    var x = 10
    
    func doSomething() {
        someFunctionWithEscapingClosure { [weak self] in
            guard let weakSelf = self else {
                
                return
            }
            weakSelf.x = 100
        }
        someFunctionWithNonescapingClosure { x = 200 }
    }
    
    deinit {
        print("deinitalised")
    }
}

var s:SomeClass? = SomeClass()
s?.doSomething()
print(s?.x ?? -1) //prints 200

completionHandlers.first?()
print(s?.x ?? -1) //prints 100
s = nil

completionHandlers.first() returns and invokes the first element of the array which is an escaping closure. Since we’d set a value type inside it using capture lists, it prints 100. We’ll be discussing escaping closures at length in a later tutorial.

One Response