Swift: DispatchGroup
An effortless way to handle unrelated asynchronous operations together.
I recently came across a situation where I needed to wait on a couple of asynchronous operations before proceeding. These operations weren’t necessarily network-related and were not interdependent. I designed an inelegant mechanism to handle this using a few booleans and callbacks but I gave up as it was too messy and it was not scalable to support as many operations as I may add later on without adding more booleans. This led me to some interesting research online and after a while, I decided up on DispatchGroup to solve my problem.
DispatchGroup
allows for aggregate synchronization of work. You can use them to submit multiple different work items and track when they all complete, even though they might run on different queues. This behavior can be helpful when progress can’t be made until all of the specified tasks are complete. — Apple’s Documentation
DispatchGroup
inherits from DispatchObject
which serves as the base class for a few other GCD-related Classes like DispatchQueue
and DispatchSource
.
Using DispatchGroup
DispatchGroup
is initialized without any parameters.
let dispatchGroup = DispatchGroup()
There’s no setup required. Just call the method enter()
on dispatchGroup
before starting an asynchronous operation and leave()
when the operation is completed.
...
dispatchGroup.enter()
invokeFirstAsynchronousOperation(completion: { _ in
dispatchGroup.leave()
})
...
Do the same for the second and subsequent operations.
...
dispatchGroup.enter()
invokeSecondAsynchronousOperation(completion: { _ in
dispatchGroup.leave()
})
...
Now, we’ve established ways of telling the dispatchGroup
object when the operations are being started and ended. The only remaining task is to let the dispatchGroup
object tell us when all the operations are completed and we’re good to go.
For this, call the notify
method on the dispatchGroup
object to get notified of completion.
...
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
//all asynchronous tasks added to this DispatchGroup are completed. Proceed as required.
})
...
The execute
closure will be called when all the asynchronous tasks are completed.
Synchronous DispatchGroup Operation
The notify
method provides an asynchronous way to get notified when all the operations get completed. But, if you need to wait on a particular thread synchronously until these operations complete, use the wait()
method.
...
dispatchGroup.enter()
invokeFirstAsynchronousOperation(completion: { _ in
dispatchGroup.leave()
})
dispatchGroup.enter()
invokeSecondAsynchronousOperation(completion: { _ in
dispatchGroup.leave()
})
dispatchGroup.wait() //Waits here on this thread until the two operations complete executing.
...
The wait
method can accept a timeout to wait for a finite maximum amount of time before continuing execution.
Points to Note
-
All code shown here is in Swift 3.0.1.
-
The number of times
leave()
has been called on a DispatchGroup object should be equal to the number oftimes enter()
was called. Otherwise, the execute closure in thenotify
method would not get called. -
The execute closure in the
notify
method would get called whenever the enter and leave counts become equal. -
The asynchronous operations added to the group can be executed on any queue. DispatchGroups work independent of that.
-
The
notify
method has a way to specify the queue on which the execute closure needs to be invoked on. This is makes DispatchGroup very versatile. -
The
notify
method is overloaded to provided different functionality, please checkout the other method definitions to see if they will better suit your needs. -
Don’t Use
wait()
on the main thread, it will freeze the interface for the user. Either usenotify
or usewait()
on a background thread.