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

Completion Handlers in Swift with Bob

I was once a code monkey. Let’s stop this madness

Problem of Mine

I often visit Stack Overflow. But, I’ve noticed people over there can be a little geeky, and many answers are terse and hard to understand based on my experience. So, here I’m, Bob the Developer, trying to save lives of many, and most importantly, prevent your hair from falling due to stress and disorder. Let’s go. 👶

Prerequisite

Completion Handlers took me months to master. It is not an easy concept if you are not good with closures. To enjoy the ride with me, you need to know trailing closures, the meaning of $0 and that you may pass or return a closure. If you are not comfortable with them, you might find Learn Swift with Bob valuable for your learning.

Why Completion Handlers

Before we dive into a technical aspect, let’s talk about what it means to use completion handlers. Assume the user is updating an app while using it. You definitely want to notify the user when it is done. You possibly want to pop up a box that says,“Congratulations, now, you may fully enjoy!”

So, how do you run a block of code only after the download has been completed? Further, how do you animate certain objects only after a view controller has been moved to the next? Well, we are going to find out how to design one like a boss.

Based on my expansive vocabulary list, completion handlers stand for,

Do stuff when things have been done

Today, we are going to focus on the word, stuff. Most beginners do not know how to create a completion handler block. I don’t blame you since I was a zombie for a significant portion of my life. Let’s get real. 🕶

Introducing Completion Handlers

Before we design one for ourselves, let’s take a look at the time you saw one previously. When you move to the next view controller, you might have seen something like this.

import UIKit
let firstVC = UIViewController()
let nextVC = UIViewController()
firstVC.present(nextVC, animated: true, completion: nil)

The completion parameter ask you to enter a closure block whose type is () -> () or () -> Void. So, let’s enter a closure block.

firstVC.present(nextVC, animated: true, completion: { () in print("Done🔨") })

“Done”, will be printed only after firstVC has moved to secondVC. To visualize, I created a small app for you.

Screen recording is too mainstream

If you are significantly confused in terms of how it works underneath, don’t worry. We will learn how to design a function like present soon. However, if you don’t know why I’ve used () in, you are in a wrong place right now. ⚠️

Since the parameter. () -> () requires no argument, and you may just print “Done” only after the transition has been completed.

firstVC.present(nextVC, animated: true, completion: {
 print("Done🔨") }
)

You can make it even shorter using a trailing closure,

firstVC.present(nextVC, animated: true) { print("Done🔨") }

If you are not familiar with trailing closure, please read Fear No Closures with Bob (Part 2). I recommend you to bring some weapons for this workplace. It can get really tough and rough. 😡

Design Completion Handler

It’s time to design a function like present above. You are now a construction worker. You tell your boss that you are going home(stuff) only after you’ve finished working.

Design Stuff Block

First, let’s create a closure block whose type is (Bool) -> (). When you pass true, it will print something.

let hanlderBlock: (Bool) -> Void = { doneWork in
 if doneWork {
  print("We've finished working, bruh")
 }
}

You may now call,

hanlderBlock(true) // "We've finished working, bruh"

But, if you want to make it shorter using $0, you may,

let handlerBlock: (Bool) -> Void = {
 if $0 {
  print("We've finished working, bruh")
 }
}

Design Function Block

Now, let’s create a function that you are working extremely hard. Once you are done, you are going to call, handlerBlock(true) to tell your boss and leave the work place like a boss.

The function will take one parameter whose type is (Bool) -> Void or (Bool) -> (). As you may have guessed it, we are going to pass handlerBlock.

To replicate that you are working really hard, we are going to call a for-in loop. Once the loop is done, you will call, the stuff block.

func workHard(enterDoStuff: (Bool) -> Void) {
 // Replicate Downloading/Uploading
 for _ in 1...1000 {
  print("👷🏻‍👷🏻👷🏽👷🏽️👷🏿‍️👷🏿")
 }
 enterDoStuff(true)
}

Again, only after you’ve done working, you are going to call enterDoStuff(true) which is eventually you are passing true to handlerBlock

When you call workHard,

workHard(enterDoStuff: handlerBlock)
Now the magic happens,
// 👷🏻‍👷🏻👷🏽👷🏽️👷🏿‍️👷🏿
// 👷🏻‍👷🏻👷🏽👷🏽️👷🏿‍️👷🏿
// 👷🏻‍👷🏻👷🏽👷🏽️👷🏿‍️👷🏿
// ...Finished the loop
// ...Worked extremely hard

// "We've done working, bruh"

Shorter Version

As you may already know, we may pass a closure block directly under enterDoStuff using a trailing closure. If you don’t know what trailing means, please read No Fear Closure with Bob (Part 2)

workHard { (doneWork) in
 if doneWork {
  print(“We’ve finished working, bruh”)
 }
}

Only after you’ve gone through the loop, doneWork will be true because you called, enterDoStuff(true) which sets the parameter block doneWork as true.

Of course, you don’t even need to create the fake parameter called, doneWork. You may just call it $0.

workHard {
 if $0 {
  print(“We’ve finished working, bruh”)
 }
}

Before you move on, make sure you really get the concept above. Do whatever it takes. Check out the source code below, read tutorials, google, and so on. You just have to get it and play around with it.

Pass Data Through Completion Handlers

This is probably the most important part. If you’ve used NSURL, any type of API that you grab JSON/Data from other platforms such as Instagram, Twitter, and so on. You’ve seen something like this,

Alamofire.request("https://image.com").responseJSON { response in
 print(response)
}

How is it even possible that the function above delivered response for me? A lot of you guys especially those who’ve used Facebook API, Firebase, and so on, probably have wondered what was going on underneath. Well, I’m here. Let’s find out.

Design Stuff Block

You are going to design a closure that takes [String] and returns Void. In other words, it doesn’t do anything.

let handler: ([String]) -> Void = { (array) in
 print("Done working, \(array)")
}

Design Function Block

First, it’s similar to what we previously did. But, we are going to change the completion block from (Bool) -> Void to ([String] -> Void)

void and () are identical

func workSuperHard(doneStuffBlock: ([String]) -> Void) {
for _ in 1...1000 {
  print(“But you gotta put in 🔨, 🔨, 🔨”)
 }
 doneStuffBlock(["Blog", "Course", "Editing", "Helping"])
}

Instead of passing true, you’ve passed[String]. In other words, when you call doneStuffBlock(["Bob"]), it becomes handler(["Bob"]), which then prints "Done working, \(array)".

Execution

It’s finally the time to call workSuperHard. This is where the magic happens.

workSuperHard(doneStuffBlock: handler)
// “But you gotta put in 🔨, 🔨, 🔨”
// “But you gotta put in 🔨, 🔨, 🔨”
// “But you gotta put in 🔨, 🔨, 🔨”
// "Done working, \(["Blog", "Course", ...])"

Of course, we may make it pass a closure directly under doneStuffBlock.

workSuperHard { (workList) in
 print("I've done \(workList[0]). Let me go 🕶")
}
// “But you gotta put in 🔨, 🔨, 🔨”
// “But you gotta put in 🔨, 🔨, 🔨”
// "I've done Blog. Let me go 🕶"

Here comes the shortest version. This isn’t recommended since there is no context in terms how the data would look like.

workSuperHard { print("Don't fire me. I did \($0[0])") }
// “But you gotta put in 🔨, 🔨, 🔨”
// “But you gotta put in 🔨, 🔨, 🔨”
// "Don't fire me. I did Blog"

That’s it.

Last Remarks

I want you to review and study over and over. It took me months to figure out closures and completion handlers. I don’t expect you to get it right away. You might need to google more. You might have to play around with the source code. But, one thing. Don’t think that you can get away with completion handlers because they are used everywhere. It is the shadow of an iOS Developer.

Learning is done by you, and when it comes to closures, you just have to play around with it until you get used to it. Good luck, and if you enjoyed or learned something new, please recommend and share so that we could turn zombies into critical human begins. 💪

Add me on Instagram @bobthedev if are interested in schedules for upcoming dope articles, tutorials, and, my daily stuff.

Source Code

About Me

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