Concurrency

How to perform parallel asynchronous operations with DispatchGroup

Learn how to fire multiple parallel URL requests as one single operation

Have you ever wanted to call multiple API endpoints at the same time and be notified when all have finished? Maybe you want to batch asynchronous operations and know when the all of the operations in the batch have finished.

There are many reasons you might want to do multiple asyncronous operations in parrallel. In this tutorial we’ll be looking at performing in parallel multiple URL requests as a single operation.

In this post we’ll learn how to use DispatchGroup to be notified when multiple concurrent asynchronous operations have finished. I assume you are familiar with the basics of Swift and Grand Central Dispatch.

I have used Swift 5.2.4 and Xcode 11.5 for this post.

Setting the context

Let’s say you have a point of sale (POS) app. In this app the user is able to create a product to sell in the store. The user can also provide a CSV file with the products to create.

When the user creates a product through the app interface the app calls the POS backend API endpoint POST /product to create a single product.

However when the user decides to create multiple products we need to call the endpoint for each product listed in the CSV file.

So how can we easily call the POST /product endpoint for each product on the CSV and get notified on completion of them all? The answer is using DispatchGroup!

Dispatch group allows you to group multiple asynchronous operations and be notified when all of them have completed.

How to perform parallel API requests using Dispatch Group

In this section we’ll implement the parallel API request feature in the already existing POS app. The POS app and server will be provided.

Here is the outline for this section:

  1. Retrieve starter project
  2. Run server
  3. Perform parallel API requests
  4. Use DispatchGroup to get notified upon API requests completion

1. Retrieve starter project

Let’s start by downloading the starter pack. Open terminal and execute the following commands:

cd $HOME
curl https://github.com/anuragajwani/dispatch-group-demo/archive/starter.zip -o dispatch_group_demo.zip -L -s
unzip -q dispatch_group_demo.zip

2. Run server

The starter project contains a server to run that will become the API which our app will communicate with and request to when creating products.

To run the server execute the following commands:

cd ~/dispatch-group-demo-starter/POSBackEnd
swift build
./.build/debug/POSBackEnd

When running the commands for the first time it can take a while to run the server. The first time the command will fetch the dependencies required to run the server.

For this project I have attached a Swift server that will act as out Products API. As it will run in our respective machines we will be easily be able to communicate with it using a simulator. However please note this won’t easily work with iOS devices.

Also note that closing this terminal window will terminate the running server. Make sure the server is running at all times during this post.

3. Perform parallel API requests

The starter project also contains an iOS app. The app is already capable of creating a single product.

In this section we’ll fill out uploadProductsCSV and add the code to create multiple products in parallel.

First let’s open the app project. In a new terminal window execute the following command:

open -a Xcode ~/dispatch-group-demo-starter/POSDispatchGroupDemo/POSDispatchGroupDemo.xcodeproj

Next we’ll read file containing products in a CSV format. The file is included in the app. We’ll read the products.csv file and convert the products from CSV into Product on the function executed when the user taps Create products from products.csv button; theuploadProductsCSV function in ViewController.swift. I have already included a convenience function named getProductsFromCSV to do so. Thus we only need to call the function and store the products into a new variable.

Open ViewController.swift and add the following line of code to uploadProductsCSV:

let products = self.getProductsFromCSV()

Next we’ll take each product within the products and create POST /product request. I have already created a convenience function that does the request. We will only be required to call it. Add the following lines of code:

products.forEach({ product in
self
.createProduct(product, onCompletion: { _ in
// TODO handle completed product creation
})
})

Note for this post we are ignoring any request errors. The CSV contains valid products and the server is running locally so there shouldn’t be any error when communicating with the server.

You should be able to run the app and create all the products on the products.csv. However we don’t have any mechanism yet to notify us when all the API requests have completed.

4. Use DispatchGroup to get notified upon API requests completion

Next we’ll use DispatchGroup to get notified when all API requests have completed.

First let’s create an instance of DispatchGroup at the beginning of the function. Add the following line of code:

let dispatchGroup = DispatchGroup()

Next before each createProduct call we need to add add dispatchGroup.enter(). On each completion of createProduct we must call dispatchGroup.leave(). The uploadProductsCSV function should look like:

@IBAction func uploadProductsCSV(_ sender: Any) {
let dispatchGroup = DispatchGroup()
let products = self.getProductsFromCSV()
products.forEach({ product in
dispatchGroup.enter()
self.createProduct(product, onCompletion: { _ in
dispatchGroup.leave()
})
})
}

dispatchGroup.enter() notifies the dispatch group that a new asynchronous call is made. dispatchGroup.leave() notifies the dispatch group that an asynchronous call has completed.

Next we’ll register a closure with the dispatch group that will be executed when all asynchronous operations have completed. At the end of uploadProductsCSV add the following line of code:

dispatchGroup.notify(queue: .main, execute: { self.showProductsCreatedAlert() })

It is important that notify is called after we tell dispatch group about all the asynchronous calls are made. If we register the closure before we notify the dispatch group about the asynchronous calls to be made then the closure will be called immediately. Why? Dispatch group calls the closure when the counter of currently executing asynchronous calls reaches 0. If we register the closure before we tell dispatch group about the asynchronous calls made the counter will effectively be at 0 and the dispatch group will execute the notify closure.

For each product sent to the server the counter will increase by 1 (when we call dispatchGroup.enter()). The counter will decrease by 1 when we finish sending a product (when we call dispatchGroup.leave()). When the counter reaches 0 the dispatch group will execute the closure registered (the one we provide to dispatchGroup.notify(queue:_, execute:_)).

And that’s it! Run the POSDispatchGroupDemo app on a simulator and tap on the Create products from products.csv button and see it in action!

There are 5 products in the CSV file. Those are the iPhones on sale at the time of writing on the Apple website and their UK prices. You can see the products being created on the terminal window running the server.

Summary

In this post we have learnt to perform parallel asynchronous and get notified upon completion of all asynchronous operations.

You can find the full source code in the repo below:

Final Notes

DispatchGroup is a great simple tool to use when you want to quickly batch multiple asynchronous operations. However currently DispatchGroup doesn’t offer any mechanism out of the box to notify us when one operation has completed and how many are left.

There are also many other ways of grouping asynchronous operation and getting notified upon the completion of each individual operation as well the whole. One example of such solution is using reactive programming framework.

Stay tuned for more posts on iOS development! Follow me on Twitter or Medium!

Senior iOS Engineer @ Onfido. Writing weekly blogs on iOS and programming. Follow me to stay tuned!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store