11

I have a situation where I need to make two HTTP GET requests and handle their results only after both are finished. I have a completion handler on each individual network request but it isn't helpful as I don't know when data from both requests are retrieved.

I have limited experience with GCD but now that Swift 3 is out, I am trying to figure out how to run multiple tasks and have a single completion handler for them. My research has shown that GCD or NSOperationQueue may be the solution I'm looking for. Can anyone help suggest which tool fits the job and what the code might look like in Swift 3?

Eman Harout
  • 536
  • 7
  • 21
  • GCD or NSOperationqueue can be used. GCD: check this out [http://stackoverflow.com/questions/37805885/how-to-create-dispatch-queue-in-swift-3][1] – user6837640 Sep 16 '16 at 15:42

2 Answers2

28

You should use dispatch groups, entering the group before you issue the request, and leaving the group in the completion handler for the request. So, let's assume, for a second, that you had some method that performed an asynchronous request, but supplied a completion handler parameter that was a closure that will be called when the network request is done:

func perform(request: URLRequest, completionHandler: @escaping () -> Void) { ... }

To start these two concurrent requests, and be notified when they're done, you'd do something like:

let group = DispatchGroup()

group.enter()
perform(request: first) {
    group.leave()
}

group.enter()
perform(request: second) {
    group.leave()
}

group.notify(queue: .main) {
    print("both done")
}

Clearly, your implementation of perform(request:) may vary significantly (e.g. you might have the closure pass the data back), but the pattern is the same whether you are writing your own networking code with URLSession or using Alamofire. Just use GCD groups, entering the group when you create the requests, and leaving the group in the completion handler of the asynchronous request.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Does group.notify's closure essentially act as a completion handler once both the tasks added to the group are finished? Also, when we enter the closures into the group, how does it know to hold off execution until we finish adding all our requests? I ask because what if it finishes the first request before I add my second, and calls notify while the second is still pending. Is this generally not a problem? – Eman Harout Sep 16 '16 at 18:26
  • 1
    Yes, the closure is called when all of the `enter` calls have been balanced with the corresponding `leave` calls. So only after all the `leave` calls are received will the dispatch group `notify` block be called. – Rob Sep 16 '16 at 18:28
  • My apologies for the barrage of questions, but wouldn't that mean it is possible that the first function leaves the group and notify is called before the second function is entered into the group? – Eman Harout Sep 16 '16 at 22:07
  • 1
    It's theoretically _possible_, but when dealing with asynchronous requests, that's extremely unlikely. And it doesn't matter if it does, because the `notify` is down at the end, by which point there have been two `enter` calls, and the group therefore won't issue the `notify` until there are two `leave` calls. This pattern is rock solid. Just make sure that you don't call `notify` until after you called all the prerequisite `enter` calls: If you do that, it won't perform the `notify` block until all the corresponding `leave` calls have been made. – Rob Sep 16 '16 at 22:12
  • @Rob I'm trying to use dispatch groups to perform a repeat while with an asynchronous request. I'm not sure how to block the while loop's condition from being checked until after I've got the response from the previous call. Is this something which could be done with dispatch groups or is there a better approach you'd advise http://stackoverflow.com/questions/41810233/asynchronous-http-request-inside-a-repeat-while-swift-3 – user2363025 Jan 24 '17 at 00:33
0

Source: How do I write dispatch_after GCD in Swift 3? You can use dispatch_group for that. For example (ObjC code):

dispatch_group_t group = dispatch_group_create();

//startOperation1
dispatch_group_enter(group);

//finishOpeartion1
dispatch_group_leave(group);


//startOperation2
dispatch_group_enter(group);

//finishOpeartion2
dispatch_group_leave(group);


//Handle both operations completion
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
//code here
});
Community
  • 1
  • 1
iDevzilla
  • 15,382
  • 1
  • 16
  • 16