Waiting on multiple asynchronous tasks

The concurrency tools on iOS and Mac are numerous and the decision about which technology to employ often comes down to the level of abstraction that the developer wants to use. For many concurrency problems, NSOperation and NSOperationQueue is sufficient.

One nearly intractable problem that I face in several applications is the need to wait on multiple web service calls to complete before starting another process. I use AFNetworking almost exclusively in my applications because it simplifies asynchronous web service work. In this case, dispatch groups and NSOperationQueue don’t work, because we only know when the job has been submitted, not when it has been completed. We need to be able to wait on multiple already-asynchronous tasks to complete. In this post, I’ll describe how I go about solving the problem.

Let’s say I have an object Foo that needs to be registered with my web service. Foo has a category that handles the registration process and executes a completion block. The solution I’m describing uses dispatch_group_enter and dispatch_group_leave along with waiting on a background queue. In this scenario, I must wait for the group to complete on a background queue because I’m firing off the asynchronous web services calls on the main queue. If I wait on the same main queue, than I block its execution and I’m deadlocked. So, my dispatch_group_wait is on a background queue.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for( Foo *foo in pendingRegistrations ) {
dispatch_group_enter(uploadGroup);
[foo uploadRegistrationWithCompletionBlock:^{
dispatch_group_leave(uploadGroup);
}];
}
// wait on another queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_group_wait(uploadGroup, DISPATCH_TIME_FOREVER);

// now I can do something that depends on all of the registration uploads
// to have been completed.

// perhaps I may even want to do those back on the main queue:
dispatch_async(dispatch_get_main_queue(), ^{
// do something on the main queue after our wait
});
});