closure function in Swift

Swift function closure, gaining the last minute Knowledge



We will continue the closure expression talk from our last blog post, closure, Starting of being creative. Here on this blog post we will address one of the confusing topic of swift, the relation between function and closure in Swift. Are they same or they are just reverse? Hmmm let us find it out.

Background

Function vs closure in Swift, the true face

Before moving on different types of closure we should have a clear idea on function and/or closure is Swift. The fact is closure is the super set and the function is the subset. So each and every function is a closure, the difference is that function is a special kind of closure in Swift. The declaration is different and obviously the behavior from typical closure. Moreover there is also nested function. Function itself need a separated talk. But for now let us have some basic knowledge on Function and closure in Swift. We need to remember function and closure are not two different things in Swift. Those two are the same thing with different form. And yes the behavior, like capturing value from scope, is different on those two.
On the following we have some points those confirm the form/outlook difference among them.

  • Function has the param and the return declaration outside of the curly brackets, whereas closure has the param and return declaration inside of the curly brackets.
  • On the function declaration there is a name for that function, such as func multiply(first: Int, second: Int) -> Int has the name multiply. But on closure there is none.
  • Function also have the func keyword prior as the declaration body, which identify it as a function. Closure don’t have any keyword to represent them as closure.
  • the in keyword differs the declaration of a closure from its body. If a closure don’t have any declaration then, no in is used. On the case of function it is always the opening curly bracket { in the mid of declaration and body.

The interesting part is closure will have different forms when it is used on function One time closure will outlive its caller function, one time it will wrap itself, one time it will

Trailing closure

The trailing part is suggesting that, this type of closure expression is related with the at the end part. Which end part? The end part of a function. So when a closure is the last param of a function then we can call that function in such a manner that, it seems like the closure is the trail part of that function. Let us have an example.

On our example we will build a function which will simply print an operation’s operands and the final output. But the final output will be a closure, which will perform an operation using those two operands.


func operationPrinter(firstOperand: Int, secondOperand: Int, operation: (Int, Int)-> Int){
    "The operation of \(firstOperand) and \(secondOperand) is \(operation(firstOperand, secondOperand))"
}

On the above code block, we are calling the operation closure using the two params, firstOperand and the secondOperand and printing them on playground. the operation closure is the last param of the function. So it will be a trailing closure when we call the function. So let us call the OperationPrinter function.


operationPrinter(firstOperand: 10, secondOperand: 20) { $0 + $1 } // 30

As we can see the calling of operationPrinter finishes with only two param, firstOperand: 10, secondOperand: 20 as the first closing brackets is presented after the second param. Interestingly after the closing of first brackets there is a closure; marked by {} and we are just doing a sum there. Oh ha if we need a better understanding of that closure syntax, then never miss the explanation.

Similarly we can write the multiplication closure.


operationPrinter(firstOperand: 10, secondOperand: 20) { $0 * $1 } // 200

Those closure seems like, they are trailing the function.

Escaping closure

The escaping keyword says it all. So this type of closure will escape or evade the return/destruction/release of the function on which the closure was sent as an argument. In other word the escaping closure is an argument of a function and the closure will outlive the function’s lifetime.

Escaping closure is marked with a @escape keyword on the function’s definition.

Q/A
Now when a closure will be an escaping closure?
Simple, when we need to call that closure on some later time. So we need to store that closure on some stored property, right? Yep right.

Q/A
Is there any particular scenario when we need to save the closer on a stored property?
Yes there is. When we do some heavy duty job on different thread other than the main and finally after completing the heavy duty job we want to send the result on the main thread. Like sorting of an array of complex data structure.
Another scenario is network job. We process the network call on separated thread and after getting the response, successful/unsuccessful, we notify the app about the response on main thread using the closure.

On the example here we will find out when we will need an escaping closure. We will define two separated method to sort an array. One will be done on main thread another using a separated thread.

We will operate on a 100 element array. Let us define the Coordinator struct. Also an array of Coordinator and populate that with random entry.


struct Coordinator{
    let x: Int
    let y: Int
}

var coordinators = [Coordinator]()
for _ in 1...100{
    coordinators.append(Coordinator(x: Int.random(in: 100...999), y: Int.random(in: 100...999)))
}

We are all set. Let us extend the Sequence, having Coordinator as the Element type, so that we can sort the sequence on main thread as following:


extension Sequence where Iterator.Element == Coordinator {
    func immediateSort(completion: ([Coordinator])-> Void){
        completion(self.sorted(by: { $0.x > $1.x }))
    }
}

Note: Generic is out of this blog post. We will have some future Blog post on Generic.

On the above extension, the closure, completion: ([Coordinator])-> Void is returned from the main thread and before the function is terminated. So on escape closure.

We can use the immediateSort as following:


coordinators.immediateSort { sorted in
    sorted.forEach({ print($0.x) })
}

Now let us consider, we have a 1000 element array. And we want to sort it on a separated thread, not on the main thread. So the extension as follows:


extension Sequence where Iterator.Element == Coordinator{
    func threadSort(completion: @escaping ([Coordinator])-> Void){
        DispatchQueue.global().async {
            let mutableEntry = self.sorted(by: { $0.x > $1.x })
            
            DispatchQueue.main.async {
                completion(mutableEntry)
            }
        }
    }
}

Note: Threading is out of this blog post scope. Again we will have some future blog post on threading.

On the above example we are sorting on a separated thread. After sorting we are submitting the result on the main thread using the completion closure. Here the completion closure has to live even after the function return. So it has to escape the function’s life span. We mark the closure as an escaping closure using the keyword @escaping.


coordinators.removeAll()
for _ in 1...1000{
    coordinators.append(Coordinator(x: Int.random(in: 100...999), y: Int.random(in: 100...999)))
}

coordinators.threadSort { sorted in
    sorted.forEach({ print($0.x) })
}

When we call the threadSort the function returns immediately. But the result is return on the closure body once the sorting is done on a separated thread.

Again we will have to use the escaping closure if we want to store the closure on a stored-property.


var completion: (([Coordinator])->Void)?

func sort(_ completionHandler: @escaping ([Coordinator])-> Void, entry: [Coordinator]){
    completion = completionHandler
    DispatchQueue.global().async {
        var mutableEntry = entry
        mutableEntry.sort(by: { $0.x > $1.x })
        
        DispatchQueue.main.async {
            if let handler = completion{
                handler(mutableEntry)
            }
        }
    }
}
sort({ sorted in
    sorted.forEach {
        $0.x
        $0.y
    }
}, entry: coordinators)

autoclosure

There are some clever ways of delaying an execution of a code block on Swift, autoclosure is one of them. Using autoclosure we can save a code block and can run that code block on some future time based on the demand.

autoclosure party looks like a regular function or a closure definition. If we are comparing autoclosure with closure then, the in part is missing with the param part. And if we are considering function then, the param part in between the first bracket (...) is missing.

So autoclosure does not take any param as functional body. It has only a code block in between two curly brackets {...}. Following two are example of autoclosure.


let success = {"Success"}
let fail = {"Error occurred"}

Simple right? we can write any code block we want in between that two curly brackets. Here for the playground project we are just printing two plain text.

That was the definition part. Now what about calling. Simple actually. Call it as we call a function, but this time obviously with no params 😉. So it should be success() and fail(). Pretty easy actually.

On the following example we will have the autoclosure execution example.


func evaluate(_ status: Bool){
    status ? success() : fail()
}

evaluate(true)
evaluate(false)

Now let us take a different path. Let us have some question.

Q/A
What type does a autoclosure have?
Well it will be a closure type, interestingly the input will always be () but the output can be anything. For success and fail it is ()->String as we are returning the String. So the return type will differ but the input/param part will always be ().

Q/A
What is the difference between success and success()?
Well, success is the ref of the closure. Just the code block.
But success() is actually executing the code block. The success() will have a type of String.

Ok back to our talk. Wait we have not use the keyword autoclosure on our code 🤔. Yep we have not. So when it will be used? Well it will be used when a closure is sent on a function as a param and we do not want to write down the closure body on the function call. Example will clarify the situation.

For the following example we will define a flag, status, as true.


let status = true

First let us have a function with out autoclosure.


func print(_ result: ()-> String){
    "Summary: \(result())"
}

As a result we have to write down the closure body when we want to call the print(_) function.


print { () -> String in
    "Based on our status, having a \(status) value, our final result is \(status ? success() : fail())"
}

The () -> String in part can be avoided on this scenario. But for this example let keep it.

Now let us have a function with autoclosure.


func prettyPrint(_ result: @autoclosure ()->String){
    "Summary: \(result())"
}

And we can call the prettyPrint() as following:


prettyPrint("Based on our status, having a \(status) value, our final result is \(status ? success() : fail())")

So the difference is we have not to write down the closure body on the function, prettyPrint(), call.

As we can see that prettyPrint has a param of String type as we are using the autoclosure on the definition of prettyPrint, ie prettyPrint(result: String).

On the other hand the print(_) has a param type of () -> String, ie print(result: () -> String).

Be careful about overusing the autoclosure, code will loose the readability.

End talk

All the source code used for the closure talk are available on GitHub. For some quick reference of closure expression syntax we can visit the fuckingclosuresyntax.

It is the same flexibility that makes closure a great friend for a developer and for some the fearful foe. For becoming a better professional we have to use that flexibility on our way, not the other way. .Happy closure talking.

3 thoughts on “Swift function closure, gaining the last minute Knowledge

Leave a Reply

Notifications for mobidevtalk! Cool ;) :( not cool