Cœur
Cœur

Reputation: 38667

How to wait for all tasks of an NSURLSession to complete?

Why is NSURLSession operation queue empty after creating and resuming an NSURLSessionTask?

Is there a way to tell if an NSURLSession has tasks pending?

The goal is to wait for multiple tasks to complete, but this doesn't work:

NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithStreamedRequest:request];
[uploadTask resume];
// this prints "0"
NSLog(self.session.delegateQueue.operationCount)
// this returns immediately instead of waiting for task to complete
[self.session.delegateQueue waitUntilAllOperationsAreFinished];

Upvotes: 3

Views: 4561

Answers (3)

Ricardo Barroso
Ricardo Barroso

Reputation: 763

In addition to @Cœur's solution, if you just need to wait for all request responses before continuing code execution you don't need to use a queue. Instead of:

dispatchGroup.notify(queue: .main) {
    // here, all the tasks are completed
}

you can simply use:

// Waits that all requests finish.
dispatchGroup.wait()

And continue code execution after this point.

Upvotes: 1

Cœur
Cœur

Reputation: 38667

I found a solution that avoids invalidating the session, using the suggested DispatchGroup.

(answer is in Swift, while question is in ObjC ... but it's the same logic)

Note that when using, uploadTaskWithStreamedRequest:, we need to implement an URLSessionTaskDelegate and func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?). So, to simplify the answer, I will demonstrate the use of DispatchGroup with uploadTaskWithRequest:from:completionHandler:.

// strong reference to the dispatch group
let dispatchGroup = DispatchGroup()

func performManyThings() {
    for _ in 1...3 {
        let request = URLRequest(url: URL(string: "http://example.com")!)
        dispatchGroup.enter()
        let uploadTask = self.session.uploadTask(with: request, from: nil) { [weak self] _, _, _ in
            self?.dispatchGroup.leave()
        }
        uploadTask.resume()
    }
    dispatchGroup.notify(queue: .main) {
        // here, all the tasks are completed
    }
}

Upvotes: 11

dgatwood
dgatwood

Reputation: 10407

It's very easy as long as you don't care about reusing the session and don't mind doing things asynchronously:

  • Call finishTasksAndInvalidate on the session.
  • Implement the URLSession:didBecomeInvalidWithError: method in your session delegate to do whatever work you need to do after the last task finishes.

That said, the problem with your code above is that the session doesn't have any operations until it starts the first fetch, which can't happen as long as your code is blocking the run loop. You generally should not attempt to use NSURLSession synchronously. It is almost always the wrong way to solve things. :-)

Upvotes: 1

Related Questions