Swift Closure

Filed Under: Swift

In this tutorial, we’ll be discussing and seeing the usages (why, how and where) of Swift Closure.
It’s noteworthy to mention here that Swift Function is also a form of closure.

Swift Closure

According to the Apple Documentation, “Closures are self-contained blocks of functionality that can be passed around and used in your code”.

Closures can capture and store references to any constants and variables from the context in which they are defined.

The concept of Swift Closure is similar to blocks in C. Closures are nameless functions and without the keyword func.

Forms of Closure in Swift

Closures can take one of the three forms.

  1. Global functions
  2. Nested functions
  3. Closures expression

Global and nested functions were discussed at length in the Functions in Swift tutorial.

When we mention closures we generally mean the third form i.e. Closure Expressions. Before we get on with the syntax and use cases, let’s look at the benefits that Closures bring to us.

Advantages of Closures in Swift

  1. Closures are much less verbose when compared to Functions. Swift can infer the parameter types and return type from the context in which the closure is defined thereby making it convenient to define and pass to a function
  2. Swift Closure can be used to capture and store the state of some variable at a particular point in time and use it later(We’ll be discussing this at length later in the tutorial)
  3. Closures allow us to run a piece of code even after the function has returned

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 ->

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. Let’s dive into playground in XCode and start coding.

Declaring Swift Closures as variables


var myClosure: (ParameterTypes) -> ReturnType

Let’s create a basic function and a closure that’ll print “Hello World”.


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.


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

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

So far so good. Lets do the same using a closure.


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.


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

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

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.


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

Let’s take it one more step ahead:


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

Add as many arguments as you can.

Note: It’s recommended to set the closure type.

It’s time to use Closures inside functions.

Closures inside Functions

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


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.


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:


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

When the closure is being passed as the final argument to a function we can pass it as a trailing closure.

A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function.

When you use the trailing closure syntax, you don’t write the argument label for the closure as part of the function call.

If closure argument is the sole argument of the function you can remove the function parentheses. Hence the previous closures can be written as the following and it would still function the same way.


operationsSq(a:2,b:2){ $0*$0 + $1*$1 }

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


func sumOfExponentials(from: Int, to: Int, myClosure: (Int) -> Int)->Int
{

    var sum = 0
    for i in from...to{
    
        sum = sum + myClosure(i)
    }
    print(sum)
    return sum

}

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

Convert a numbers array to strings array
Another use case of trailing closure is given below.


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

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

map is a high order function for transforming an array.

Sort numbers in descending order using trailing closures


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]\n"

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.


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

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


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 as 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.


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.


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:
"Initial value is not defined yet" followed by ” escaped” (“__lldb_expr_26.Person escaped” for me). Awesome there are no memory leaks. Lets set the value inside myClosure to x as shown below:


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.

  • A weak reference is a reference that does not keep a strong hold on the instance it refers to and so does not stop ARC from disposing of the referenced instance.
  • An unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime.

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


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:

  • An escaping closure is a closure that’s called after the function it was passed to returns. In other words, it outlives the function it was passed to. An escaping closure is generally used in completion handlers since they are called after the function is over.
  • A non-escaping closure is a closure that’s called within the function it was passed into, i.e. before it returns. Closures are non-escaping by default

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.

Here’s a screenshot from my playground.
swift closure

That’s all about closure in swift.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages