I have an app that currently uses NSURLConnection
for the vast majority of its networking. I would like to move to NSURLSession
because Apple tells me that is the way to go.
My app just uses the synchronous version of NSURLConnection
by way of the + (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
class method. I do this within a NSBlockOperation
running on an NSOperationQueue
so I am not needlessly blocking the main queue. The big advantage to doing things this way is that I can make the operations dependent on one another. For example, I can have the task that is requesting data be dependent on the login task finishing.
I have not seen any support for synchronous operations within NSURLSession
. All I can find are articles deriding me for even thinking of using it synchronously and that I am a horrible person for blocking the threads. Fine. But I see no way to make NSURLSessionTask
s dependent on each other. Is there a way to do that?
Or is there a description of how I would do such a thing in a different way?
@Rob I would encourage you to post your reply as a solution, in view of the following documentation note from
NSURLSession.dataTaskWithURL(_:completionHandler:)
:The harshest criticisms of synchronous network requests are reserved for those who do it from the main queue (as we know that one should never block the main queue). But you're doing it on your own background queue, which addresses the most egregious problem with synchronous requests. But you're losing some wonderful features that asynchronous techniques provide (e.g. cancelation of requests, if needed).
I'll answer your question (how to make
NSURLSessionDataTask
behave synchronously) below, but I'd really encourage you to embrace the asynchronous patterns rather than fighting them. I'd suggest refactoring your code to use asynchronous patterns. Specifically, if one task is dependent upon another, simply put the initiation of the dependent task in the completion handler of the prior task.If you have problems in that conversion, then post another Stack Overflow question, showing us what you tried, and we can try to help you out.
If you want to make an asynchronous operation synchronous, a common pattern is to use a dispatch semaphore so your thread that initiated the asynchronous process can wait for a signal from the completion block of the asynchronous operation before continuing. Never do this from the main queue, but if you're doing this from some background queue, it can be a useful pattern.
You can create a semaphore with:
You can then have the completion block of the asynchronous process signal the semaphore with:
And you can then have the code outside of the completion block (but still on the background queue, not the main queue) wait for that signal:
So, with
NSURLSessionDataTask
, putting that all together, that might look like:With
NSURLConnection
(now deprecated), you have to jump through some hoops to initiate requests from a background queue, butNSURLSession
handles it gracefully.Having said that, using block operations like this means that the operations won't respond to cancellation events (while they're running, at least). So I generally eschew this semaphore technique with block operations and just wrap the data tasks in asynchronous
NSOperation
subclass. Then you enjoy the benefits of operations, but you can make them cancelable, too. It's more work, but a much better pattern.For example:
and
Where:
And