Hello everyone, Leo here. Today we’ll see a very common problem in computer science, and yes is about asynchronous programming. There’s a lot to explore inside Apple’s GCD framework but today we’ll dive into a class called DispatchGroup and analyse what it can do for us. We will check various examples of DispatchGroup in practice and how they can improve your codebase.

No more talking(writing?), let’s code.

DispatchGroup in Practice

The problem:

You have various asynchronous calls and you want to have a single completion handler when all calls are done. Imagine that you have to download various resources and only after all have finished you could enable a button to continue the app flow.

This task would be a little complicated if you had to code this behavior yourself. Luckily we have the DispatchGroup class to achieve that!

From the DispatchGroup definition docs:

Groups allow you to aggregate a set of tasks and synchronize behaviors in the group. You attach multiple work items to a group and schedule them for asynchronous execution on the same queue or different queues. When all work items finish executing, the group executes its completion handler. You can also wait synchronously for all tasks in the group to finish executing.

Let’s examine the code below:

let queue = DispatchQueue.global() //1
let dispatchGroup = DispatchGroup() //1

dispatchGroup.enter() //2
queue.async(group: dispatchGroup) {
    sleep(3) // simulate a heavy backgroud task
    print("heavy async task 1 - done")
    dispatchGroup.leave() //2
}

dispatchGroup.enter() //2
queue.async(group: dispatchGroup) {
    sleep(1)
    print("heavy async task 2 - done")
    dispatchGroup.leave() //2
}

dispatchGroup.notify(queue: queue) { //3
    print("now we can handle when everything is done, ex: setup a button animation to appear")
}

print("All work started!")

So we’re looking at:

  1. First we have to create the variables queue and the dispatchGroup.
  2. For every heavy task we need to call the *dispatchGroup.enter()* and when the job is finished don’t forget to call the *dispatchGroup.leave()*.
  3. When all the groups are finished we run the *dispatchGroup.notify(queue: queue)*, this enables handling all the different async threads with one completion handler.

This should appear on your console log:

I recommend you run this code in the playground to understand it better.

And you can use DispatchGroup inside a for a loop too:

let queue = DispatchQueue.global()
let dispatchGroup = DispatchGroup()

for num in 0...2 {
    dispatchGroup.enter()
    queue.async(group: dispatchGroup) {
        sleep(UInt32(Int.random(in: 1...3)))
        print("download archive \(num) - done")
        dispatchGroup.leave()
    }
}

dispatchGroup.notify(queue: queue) {
    print("now we can handle when everything is done, ex: setup a button animation to appear")
}

print("All work started!")

And we are done with our two examples of how to use DispatchGroup!

Summary

Today we learned about how to do a completion handler to many async calls. In later articles about threading we’ll explore semaphores and how they can be very useful to handle shared resources inside your app, so don’t miss it!

That’s all my people, I hope you liked reading this article as much as I enjoyed writing it. If you want to support this blog you can Buy Me a Coffee or leave a comment saying hello. You can also sponsor posts and I’m open to freelance writing! You can reach me on LinkedIn or Twitter and send me an e-mail through the contact page.

Thanks for the reading and… That’s all folks.

Credits: image