No Fear Closure in Swift 4 with Bob (Part 2)

@escape, Trailing closure, completion handlers

Bob Lee
Bob the Developer

--

Once you’ve tasted closures, you never go back.

Last update on May 15th, 2017 | Swift 3.1

Anyway, let’s learn Closures. In the first lesson, you’ve learned closures and functions are like siblings. They are identical under the hood (DNA) even though they look a bit different. You’ve also learned you can pass closures/functions as arguments/parameters and return them. So far, you’ve laid a great foundation for constructing a solid tower— learning reactive/functional programming in Swift.

Motivation

Well, too many iOS developers (me, basically) simply memorize and copy off from Stack Overflow when we see words like completion handlers. It often occurs when you try to use animation, fetch data from third party APIs, and so on. Therefore, this tutorial is written for my younger self how to create our own completion handlers and explain what they are, and what’s the heck is going on underneath.

What I think you will learn

The meaning of completion handlers, when to use@escaping and trailing closure. It could be overwhelming, but I will try my best to explain these concepts using analogy and simple but concise examples. I’ve also put some resources and source code at the end of this tutorial. Let’s begin, brothers, sisters, or if you ain’t comfortable, lovely humans.

Trailing Closure

I’m a big fan of figuring out etymology (name) so that I understand without memorizing. But, let’s talk about what “trailing closure” is in the first place.

Well, again, if I were to explain it to my 13-year old sister, trailing closures are like… Damn, I can’t think of any. Instead, let’s just compare normal closures vs trailing closures, and see what we can do.

Normal Closure

Let’s create a function that add a range of values whose each value is multiplied by 10. The last argument of the function takes a closure that multiplies each input. So, you put the first parameter as 0 through 10, and you will get 0 + 10 + 20 + 30 + …. 100 = 550

So first, let’s begin by creating a closure that takes inputs and then multiplies them by 10. There are two ways. 1. Closure using the func or 2. just store it as a value straight (cover in Part 1).

// Global Closure (a.k.a function) 
func timesTenFunc(number: Int) -> Int { return number * 10 }
// Normal Closure
var timesTenClosure: (Int) -> Int = { $0 * 10 }
// Test👆
timesTenFunc(number: 10) // 100
timesTenClosure(10) // 100

Okay, now it’s time to create a function that sums a range of values. Of course, we are going to add timesTenFunc or timesTenClosure as the final parameter. (Example driven from the Swift doc)

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

Now, if I try to run this

sum(from: 0, to: 10, closure: timesTenFunc) // 550
sum(from: 0, to: 10, closure: timesTenClosure) // 550

Or, if you want to insert the final parameter straight,

// Longer Version
sum(from: 0, to: 10, closure: { number in return number * 10 })
// Shorter Version
sum(from: 0, to: 10, closure: { $0 * 10 })

Trailing Closures

Now, let’s see how to reinvent our example using a trailing closure. The reason I elaborated on the previous example was to recap how closures work because I didn’t want to overwhelm my readers from 0 to 100 real quick.

Just like the sum example above, a trailing closure can be used only if a function requires a closure as the final argument/parameter. You may trail (to follow behind) the closure argument parameter section. In other words, the final parameter is separated from the normal function parameter block.

// Trailing Closure Longer Version
sum(from: 0, to: 10) { (number) in
return number * 10
}
// Trailing Closure Shorter Version
sum(from: 0, to: 10) { return $0 * 10}
// Trailing Closure Extremely Short Version
sum(from: 0, to: 10) { $0 * 10 }

So, what’s the advantage of using trailing closures? Well, it is particularly useful when your closure expression is longer than just simply multiplying values or when you are using completion handlers, which I will talk about them soon in this article. Trailing closures make code readable and eye friendly since it clearly indicates it’s a different animal.

Just to clear things up, you may use a normal closure within a function. A trailing closure is optional, but most developers love/use it.

Completion Handler

Me: If you were to take away one thing, please understand this concept.

There are times you put a closure as one of the parameters, but you only want to execute the closure after the function returns. For example, what if you want to print “Hello, world” only after you’ve completely switched to the next ViewController?

If normal cases, you will see

self.present(nextViewController, animated: true, completion: nil)

But, to print “Hello world”, only after nextViewController is presented,

self.present(nextViewController, animated: true, completion: {
print("Hello World")
})

The content has been fully migrated from Medium to the personal blog. If you wish to finish reading the rest of the article and learn about closures, feel free to visit the new platform here.

--

--