CloseHomeAboutBlogCourse
Bob I’m an iOS instructor/blogger from S.Korea.July 14 • 5 min read • Edit

No Fear Closure in Swift with Bob Part 2

@escape, Trailing closure, completion handlers

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")
})

Since the type of the closure we need to pass under completion is () -> Void we can directly put the print statement without worrying about input nor returning values within the closure block.

Hello World only gets printed once you’ve completely presented the next viewcontroller.

We call the completion block as “Completion Handlers” since I guess we do extra work only after a function has completed its work, and you want to handle with some spice to it. The concept of completion handlers is used for animation and notifying users when the download has been completed.

Great, Let’s Make One

Sounds good. Let’s make one. We are going to create our own function that contains a completion handler that if the download has been successful, you are going to notify the user.

// Create Function
func downloadFileFromURL(url: NSURL, completionHandler: (Bool) -> Void) {
 // NSURL logic code & download. (return false/true)
 // Download has been completed
 completionHandler(true)
}

Surprisingly, even before we call the function downloadFileFromURL, we’ve provided a default parameter to the completionHandler block by defining it as true only if the networking logic has successfully gone through. So, when you run the function, the only parameter you need to fill up is url (It’s just Swift’s magic) and you can notify the user based on whether the completionBlock is true or false. It may not make sense right now, so let’s take a look.

// Run function
downloadFileFromURL(url: NSURL(string: "abc")!, completionHandler: {
 isSuccessful -> Void in
  if isSuccessful {
   print("You've downloaded")
   } else {
    print("Unexpected error encountered")
  }
})

I have arbitrarily called the predefined the name of the parameter (true/false) as isSuccessful. The cool thing is, again, isSuccessful already has its own value because we predefined while creating the function. However, please remember that isSuccessful is only defined after going through the network logic. This example can be super confusing because you might be not used to using a block of code that is pre-defined within a function. But, you just have to deal with it.. :)

In Real Life Situation

So, when you try to fetch data from Firebase/Facebook, you will always ask to put something like

fetchProfileImage() { (imageFile, name, error) in
 if error == nil {
  // put into UIImage with imageFile/name
 }
}

So, you’ve renamed imageFile, name, and error and you can use them however you want it when there are values associated with them just like isSuccessful. Pretty awesome?

Escaping Closures

I haven’t used escaping closures so I don’t think I can give you a practical example (You guys can help me), but I can tell you how it works. What does it mean by escaping? It is used when you want to input a closure as the parameter and the closure is called after the function block returns/finishes So, let’s take a look.

So, I’ve created an array closure/functions.

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

So, we are going to create a function that inserts a closure into the completionHandlers array.

// It doesn't work!
func functionWithCompletionHandler(block: () -> Void) {
 completionHandlers.append(block)
}

// Escaping Closures and works! func functionWithCompletionHandler(block: @escaping () -> Void) {
 completionHandlers.append(block)
}

However, the block closure is what the user inputs, but it happens to escape out of functionWithCompletionHandler by appending into the completionHandlers array that isn’t atatached to the function block. So, you must indicate that the block closure, will escaped out of the function.

// run the function
functionWithCompletionHandler(block: { print("hello") })

completionHandlers[0]() // "hello"

Last Remarks

In this article, I’ve briefly talked about hypothetical examples. But, if you wish to know more about @escape and autoclosures, how they are important for memory management, feel free to join the Swift intermediate course called, “Learn Swift with Bob".

Resources

Source Code

About Me

iOS Developer from South Korea. Feel free to follow my story on Instagram or get serious on LinkedIn