Using DispatchGroup in Swift for asynchronous loops
31 Dec 2020
Occasionally in an iOS app you’ll do something asynchronous, calling a completion handler when it’s done, like this:
itemProvider.loadObject(ofClass: UIImage.self) { image in
onComplete(image)
}
This works great, but what if we have an array we need to loop through, performing an asynchronous action for each one? You could try this:
var images = [UIImage]()
for itemProvider in itemProviders {
itemProvider.loadObject(ofClass: UIImage.self) { image in
images.append(image)
}
}
onComplete(images)
..but that won’t work, because the onComplete
handler will fire before the asynchronous blocks execute.
For situations like this, we can use Swift’s DispatchGroup
.
We create a DispatchGroup
before starting the loop, enter it each time the loop iterates, and leave it when each iteration ends. Finally, we notify the dispatch group that everything is complete. Let’s see how that looks in practice:
let dispatchGroup = DispatchGroup()
var images = [UIImage]()
for itemProvider in itemProviders {
dispatchGroup.enter()
itemProvider.loadObject(ofClass: UIImage.self) { image in
images.append(image)
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main) {
onComplete(images)
}
Now, everything works as it should and the completion handler isn’t executed until after the loop has finished iterating.
Thanks for reading.
To get in touch, email me or find me on Mastodon or Twitter.
If you liked this post, you'll love my iOS apps. Check them out below.