loading...

Swift: Easy way to wait for multiple background tasks to finish

nemecek_f profile image Filip Němeček ・1 min read

Suppose you need to wait for multiple network requests before executing some other action like saving the data in one batch or populating the UI. Swift offers great solution named DispatchGroup.

This is very straightforward class to use and in this post I will show you how.

The DispatchGroup basically functions like a kind of counter of running operations. You call the method enter() to signal that task begun and then call leave() when the work is finished.

You get notified once all calls to leave() are done ✅

Let's check this example below:

let group = DispatchGroup()
for item in self.items {
        group.enter()
        self.downloadData(for: item) { (data, error) in
             // handle data and possible error here                
             group.leave()
        }
}

group.notify(queue: .main) {
        // all data available, continue
}

In this example the downloadData() method has completion handler that finishes after some time.

Once data for all the items are downloaded, the notify() will be triggered and you can respond.

Important: You need to make sure to always call leave() when the work is done otherwise the notify() will never be called. You can use defer keyword if you have more complex logic.

defer will execute the specified block right before the end of the current scope so you don't have to handle multiple code paths due to conditions, errors etc.

You can also use group.wait() which will wait for all items to finish before the execution will continue. This is only for background threads. If you call it on your main thread, you will block the UI until the work is done. Be careful. ❗

Thanks for reading!

Posted on Apr 6 by:

nemecek_f profile

Filip Němeček

@nemecek_f

Primarily iOS developer, I also like Django and Python. And dabble with JavaScript occasionally. Love reading and coffee.

Discussion

markdown guide
 

Thanks.
One thing though, is that relying on DispatchGroup - while being great - does not allow to control the number of threads that will be created on the queue. It can lead to thread explosion depending on the job done by the work items. If you have many tasks that should run in parallel, you need to take this into account and maybe use a OperationQueue instead. Using dependent operations on an OperationQueue, you can also wait for all operations to finish before continuing.

 

Thank you for the additional insight. I thought DispatchQueue is pretty smart when it comes to handling threads and generally you dont have to worry about it.. Would love to read more about OperationQueue if you have more thoughts & tips to share!

 

Well it depends. If your work that is dispatched does not seem to make enough progress, the OS might decide to spawn more threads and you could hit that soft limit and the app could crash. Probably never in your use case where you download a few files. But my use case is a collection of several 10,000 files that need to be accessed for metadata in the background. I used a DispatchGroup to wait for everything to complete before moving to the next step but I was hit hard by thread explosion. Randomly. I moved to NSOperationQueue and added a limit on the number of concurrent operations. There is also a lesser known command called DispatchQueue.performConcurrent(...) that is useful and will limit the number of threads to 8.
This article, albeit a bit old, is good: agostini.tech/2017/08/20/dispatchg...