Using DispatchGroup in Swift for asynchronous loops

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.

Personal Best

Level up your workouts

Taylor's Version

Upgrade Taylor Swift songs in your playlists

SalaryPig

Meet Trevor, the salary-tracking pig

Taylor Swift Quiz

How well do you know Taylor Swift?