TLDR
Dispatch groups allow us to manage and group multiple async tasks together and wait for all of them to finish before continuing with the rest of our code.
Building fast and responsive apps often involves managing multiple asynchronous operations. In the earlier days of Swift, before the introduction of async/await
, coordinating these operations and ensuring they were completed in a synchronized manner was challenging. However, Swift’s Dispatch Group
emerged as one of the primary tools for performing asynchronous tasks and grouping them together. In this article, we’ll explore what Dispatch Groups are and how they have been instrumental in coordinating and synchronizing multiple asynchronous operations in many legacy iOS apps.
Why Use Dispatch Group?
In asynchronous programming, it is common to have a situation where you need to perform several tasks concurrently and then wait for all of them to complete before proceeding further. Dispatch Group provides a way to achieve this coordination [prior to async/await that is].
A Dispatch Group allows us to submit multiple tasks to a concurrent dispatch queue and wait for all of them to finish. It provides a structured way to monitor the progress of these tasks and to be notified when they have all completed.
Usage
Here’s how a Dispatch Group works:
- Creating a Dispatch Group: You create an instance of DispatchGroup using the
DispatchGroup()
initializer.
1
let group = DispatchGroup()
- Submitting Tasks to the Dispatch Group: As you initiate your asynchronous tasks or operations, you associate them with the Dispatch Group by calling
enter()
method before starting each task. This tells the group that a task is about to begin.
1
2
group.enter()
// Start async task 1
- Leaving the Dispatch Group: When each task completes, you call
leave()
to indicate that the task has finished. This balances the previousenter()
call.
1
2
// Task 1 completion
group.leave()
- Waiting for Tasks to Complete: To wait for all tasks in the group to finish, you call
wait()
on the Dispatch Group. This blocks the current thread until all the associated tasks have completed.
1
2
group.wait()
// All tasks have completed
- Completion Handler: You can also provide a completion handler to be executed when all the tasks have finished, using the
notify(queue:work:)
method. This allows you to perform additional operations or handle the results of the completed tasks.
1
2
3
4
group.notify(queue: .main) {
// All tasks completed
// Perform additional operations or handle results
}
Putting it all Together: Example Scenario
Let’s consider an example scenario where you need to fetch data from multiple APIs concurrently and process the results only when all requests have completed. Here’s how you can accomplish this using Dispatch Group:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
let group = DispatchGroup()
let queue = DispatchQueue.global()
// Fetch user profile
group.enter()
queue.async {
fetchUserProfile(userID: "123") { userProfile in
// Process the user profile
group.leave()
}
}
// Load posts
group.enter()
queue.async {
loadPosts { posts in
// Process the posts
group.leave()
}
}
// Download media
group.enter()
queue.async {
downloadMedia(post: somePost) { media in
// Process the downloaded media
group.leave()
}
}
// Handle completion
group.notify(queue: .main) {
// All tasks completed
// Update UI or perform additional operations
}
// Wait for all tasks to complete
group.wait()
// Additional code after all tasks have completed
In the above example, we create a Dispatch Group, associate each asynchronous task with the group using enter()
, and call leave()
methods when each task completes. Then we get notified by the group’s notify(queue:work:)
method and use this to perhaps perform UI updates or additional operations. Finally, we can wait for all tasks to finish using the group’s wait()
method, it’s important to note that the wait()
method will block the thread and synchronously wait for the previously submitted work to finish before proceeding to the next.
And just like that we have leveraged the Grand Central Dispatch
(GCD) framework to coordinate and synchronize multiple asynchronous operations.