Functional swift: All about Closures

According to apple docs: Closures are self-contained blocks of functionality that can be passed around and used in your code. I tried to include all about closures here in this article. It’s a bit lengthy one. But worth reading it. I guarantee !!!

Image for post
Image for post
I don’t know who that is, But the photo looks refreshing. isn’t??

Some people say that Functions and closures are the same thing with different syntax. Others say that functions are special kind of closure.

Closures are first-class objects, so that they can be nested and passed around

First, let us know what a function is:

Function:

Image for post
Image for post
function that takes two parameters and returns two parameters using tuples

The above function will take two integers as input and returns a tuple with two integer values. If you have no experience with a tuple, please check my other article here.

Function types

func doSometingWithInt(someInt:Int) -> Int {return someInt * 2}

Every function has its own function type, made up of the parameter types and the return type of the function itself. The function type of the above function will be :

(int) -> (int)

If the function takes two parameters, and return one parameter, the type will be:

(int, int) -> (int)

This function type can be used as return type from nested functions or as parameters.

func personInTheHouse() -> ((String) -> String) {func doProcess(process: String) -> (String) { // nested functionreturn “The person is \(process).”}return doProcess // or return doProcess(process:)}let person = personInTheHouse()print(person(“playing cricket”)) // prints “The person is playing cricket.

Defining a closure :

{ (params) -> returnType instatements}

Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context. Closure expression syntax is given in the code block above.

  • Closure expressions provide several syntax optimizations for writing closures in a shortened form without loss of clarity or intent.
  • The parameters in closure expression syntax can be in-out parameters, but they can’t have a default value. Variadic parameters can be used if you name the variadic parameter. Tuples can also be used as parameter types and return types.

Let’s create a function which accepts two int values and return the sum as int.

func addTwoNumbers(number1:Int, number2:Int) -> Int {return number1 + number2}addTwoNumbers(number1: 8, number2: 2) // result is 10

Now let’s create a closure for the same operation:

let closure: (Int, Int) -> Int = { (number1, number2) inreturn number1 + number2}closure(8,2) // the result is 10

The function and closure looks more or like similar in the number of lines of code, readability etc.. Lets try to simplify the closure even simpler.

Shorthand Argument Names

var shortHandClosure:(Int,Int)->Int = {return $0 + $1}shortHandClosure(8,2) // result is 10

Since , the closure’s body contains a single expression which returns an Int value, we can omit the return statement. If there are multiple lines of code inside the closure body, then we cannot omit the return statement.

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

Now, this looks very much different from the function that we defined in the beginning . This is much simpler and more meaningful as well.

Note: If we had to write an additional print statement along with the return expression, we cannot omit the return statement.

Our objective is to write less code.

Inferred type closure

let inferredClosure = {(x:Int,y:Int)->Int in x + y }inferredClosure(1,99) // result is 100

Return types can also be inferred:

let inferredReturnTypeClosure = {(number:Int) in number*number }

What do you expect the type of the above closure to be?, It returns an int .

It is (Int) -> Int

Closure that takes nothing and returns a string:

The following is a closure that takes no parameters and returns a string.

let callStringWtihClosure: () -> String = { () in return “hello”
}
//____________
print(callStringWtihClosure()) // prints “hello”

Since, we don’t care about the input parameters here, we can omit the () in inside the closure body.

The closure type for the one below will be () -> String

let callStringWtihClosure: () -> String = {return “hello”}

Also, since it returns a String and it doesn’t take any parameters, we can omit the type as well.

The closure type for the one below will be () -> String

let callStringWithClosureWithoutType = { “hi, I’m a closure too” }

Closures and functions are first class types in swift:

  • assign a function/closure to a local variable .
  • pass a function/closure as an argument .
  • return a function/closure .

Methods with completion callback using closures:

The following code has a function which has three parameters. One is a dictionary , other two are closures which will act as callback functions after when the process is done.

the @escaping keyword before the closure in the function definition will be explained in the following sections.

Returning a closure from a function

// return a closure from a functionvar addClosure:(Int,Int)->Int = { $0 + $1 }func returnClosure() -> (Int,Int)->Int {return addClosure}//____________________________________returnClosure()(10,20) // returns 30var returnedClosure = returnClosure() // returns a closure of type (Int,Int)->IntreturnedClosure(20,10) // returns 30

Initialize or customize objects using closures

Create a UIView ,customize it using a function and return it. Create a function of type () -> UIView .

func setupView() -> UIView {let view = UIView()view.backgroundColor = .redreturn view}let someView = setupView() // returns a red view

Now, do the same with a closure of type () -> UIView . In the below code setupViewUsingClosure is a closure of type () -> UIView .

let setupViewUsingClosure = { () -> UIView inlet view = UIView()view.backgroundColor = .greenreturn view}let someOtherView = setupViewUsingClosure() // returns a green view

The above method can further be simplified into the following syntax where we can directly initialize an object using a closure.

let setupViewUsingClosure: UIView = {let view = UIView()view.backgroundColor = .greenreturn view}() //IMPORTANT!!! I have added () at the end.

In the above code setupViewUsingClosure is a UIView object . The code towards the RHS of the expression is actually a closure of type () -> UIView . Let’s make it more clear.

let aClosure= { “hello, I’m a closure too.” }let aString= { “hello, I’m a closure too.” }() // added () at the end.

In the first statement, aClosure is actually a closure of type ()->String .

IN the second statement, we added () at the end .So it became a closure call. So, it executes the closure and returns a String . If it is not clear, refer the Inferred type closure section above.

lazy init:

Trailing Closures

func doSomething(number:Int,onSuccess closure:(Int)->Void) {closure(number * number * number)}doSomething(number: 100) { (numberCube) inprint(numberCube) // prints  1000000}

The argument label onSuccess is not there in the function call. Even though the closure is included in the function parameters list, swift will take it out of the parameter block to make the code more readable.

Capturing values

Consider the following code:

// capturing valuesvar i = 0var closureArray = [()->()]()for _ in 1…5 {closureArray.append {print(i)}i += 1}// here i will be 5closureArray[0]() // prints 5closureArray[1]() // prints 5closureArray[2]() // prints 5closureArray[3]() // prints 5closureArray[4]() // prints 5

For every iteration of the for loop, we created an empty closure ()->() and added it to an array called closureArray . Closure body contains only a single statement which prints i . The closure captures the current address of i and every time we access i , it . returns the current value.

As mentioned above, the memory issues related to this capturing is handled by swift. We don’t have to worry about it.

Important points are:

  • A closure can capture constants and variables from the surrounding context in which it is defined.
  • The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.
  • Swift also handles all memory management involved in disposing of variables when they are no longer needed.
  • If you assign a closure to a property of a class instance, and the closure captures that instance by referring to the instance or its members, you will create a strong reference cycle between the closure and the instance. Swift uses capture lists to break these strong reference cycles.

Creating a capture list:

var closureArray2 = [()->()]()var j = 0for _ in 1…5 {closureArray2.append { [j] inprint(j)}j += 1}// here i will be 5closureArray2[0]() // prints 0closureArray2[1]() // prints 1closureArray2[2]() // prints 2closureArray2[3]() // prints 3closureArray2[4]() // prints 4

In this way, we keep an immutable copy of the variable j. Thanks to this copy, further changes to j, outside the closure, will not affect the closure. J is a let constant here. It is not mutable.

We can add multiple values to the capture list :

closure.append { [j,k,l] inprint("\(j) \(k) \(l)")}

also, you can have alias names for the values captured.

closure.append { [a = j, b = k, c = l] inprint("\(j) \(k) \(l)")}

Escaping closure vs non-escaping closure

  • 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.
  • A non-escaping closure is a closure that’s called within the function it was passed into, i.e. before it returns.

@noescape was an attribute in swift 2. This is deprecated from swift 3. The @noescape attribute is applied by default in Swift 3. Because closures are by default non-escaping in Swift 3, escaping closures need to be marked as such. And the @escaping attribute lets us do that.

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

A good example of an escaping closure is a completion handler. It’s executed in the future, when a lengthy task completes, so it outlives the function it was created in. Another example is asynchronous programming: a closure that’s executed asynchronously always escapes its original context.

func someFunctionWithNonescapingClosure(closure: () -> Void) {closure()}func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {completionHandler()}class SomeClass {var x = 10func doSomething() {someFunctionWithEscapingClosure { self.x = 100 }someFunctionWithNonescapingClosure { x = 200 }}}

You may have noticed that the @escaping attribute precedes the type of the parameter, not the name. This too is new in Swift 3.

Marking a closure with @escaping means you have to refer to self explicitly within the closure. For example, in the code below, the closure passed to someFunctionWithEscapingClosure(_:) is an escaping closure, which means it needs to refer to self explicitly. In contrast, the closure passed to someFunctionWithNonescapingClosure(_:) is a nonescaping closure, which means it can refer to selfimplicitly.

You can use the self keyword without issues in non-escaping closures because the closure is invoked before the function returns. There is no need to use a weak reference to self in the closure. This is a nice benefit you get for free.

Memory management

A strong reference cycle occurs when two objects keep a strong reference of each other. Because of this cycle, the two objects won’t be deallocated, since their reference count doesn’t drop to zero. By default, a closure keeps a strong reference of the variable captured.

Consider the following example.

class InterviewTest {var name: String = “Abhilash”lazy var greeting : String = {return “Hello \(self.name)”}()}
//-------------------------
let testObj = InterviewTest()testObj.greeting // result is Hello Abhilash

The lazy var greeting is returning a string by accessing the local variable name from the class InterviewTest . We cannot directly access the variable. We have to use self keyword to do that. But as explained, by default, a closure keeps a strong reference of the variable captured. It may cause reference cycles.

We can break this strong reference cycle using self in the capture list with either a weak or an unowned reference.

weak

Since a weak reference may be nil, the variable captured becomes optional. Therefore, we should use a guard to safely unwrap it:

lazy var greeting : String = { [weak self] inguard let strongSelf = self else { return “” }return “Hello \(strongSelf.name)”}()

Unowned

lazy var greeting : String = { [unowned self] inreturn “Hello \(self.name)”}()

This means that we should use unowned just when we are sure that the reference will never be nilinside the closure, otherwise the app would crash. So, guard check is not required here.

We can use weak and unowned with any variable in the capture list and we can also combine with the aliases:

lazy var greeting : String = { [unowned unownedSelf = self] inreturn “Hello \(unownedSelf.name)”}()

How to capture self with closures with arguments?

Image for post
Image for post
capturing self along with closure parameters

READ MORE ABOUT CLOSURES HERE.

Autoclosures

Overusing autoclosures can make your code hard to understand.

var customersInLine = [“Chris”, “Alex”, “Ewa”, “Barry”, “Daniella”]print(customersInLine.count)// Prints “5”let customerProvider = { customersInLine.remove(at: 0) } // this is of type ()->Stringprint(customerProvider()) // prints Chris.. the remove(at:) returns a String.

Consider the above function passed on to a function as argument.

// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]func serve(customer customerProvider: () -> String) {print("Now serving \(customerProvider())!")}serve(customer: { customersInLine.remove(at: 0) } ) // we cannot omit {}// Prints "Now serving Alex!"

The serve(customer:) function in the listing above takes an explicit closure that returns a customer’s name. The version of serve(customer:) below performs the same operation but, instead of taking an explicit closure, it takes an autoclosure by marking its parameter’s type with the @autoclosure attribute. Now you can call the function as if it took a String argument instead of a closure. The argument is automatically converted to a closure, because the customerProvider parameter’s type is marked with the @autoclosure attribute.

// customersInLine is ["Ewa", "Barry", "Daniella"]func serve(customer customerProvider: @autoclosure () -> String) {print("Now serving \(customerProvider())!")}serve(customer: customersInLine.remove(at: 0))// Prints "Now serving Ewa!"

If you want an autoclosure that is allowed to escape, use both the @autoclosure and @escaping attributes.

So, in short,

serve(customer: { customersInLine.remove(at: 0) }) // need {}

becomes…..

serve(customer: customersInLine.remove(at: 0)) // omit {}

One more example for explaining autoclosure:

extension UIView {

class func animate(withDuration duration: TimeInterval, _ animations: @escaping @autoclosure () -> Void) {
UIView.animate(withDuration: duration, animations: animations)
}

}

So that,

UIView.animate(withDuration: 2.5) { 
self.view.backgroundColor = .orange
}

becomes….

UIView.animate(withDuration: 2.5, self.view.backgroundColor = .orange)

Last , but not the least!!!

Functions and closures are reference types. Here , in the following code, both addClosure2 and addClosure refers to the same closure in memory.

var addClosure:(Int,Int)->Int = { $0 + $1 }let addClosure2 = addClosure

Enjoy!!

If you enjoyed reading this post, please share and recommend it so others can find it 💚💚💚💚💚💚 !!!!

If you have any comment, question, or recommendation, feel free to post them in the comment section below!

Written by

iOS and tvOS developer, dreamer, photographer 🤨

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store