NSURLSession & synchronous requests

10 Dec

When, with the advent of iOS 7, NSURLSession was introduced to take the place of NSURLConnection the developer community welcomed joyfully the change… unfortunately despite being a big step forward in terms of Networking API NSURLSession  is lacking something its ancestor has: a way to perform synchronous requests.

I perfectly know that a good user experience requires that all network tasks should be asynchronous to avoid locking the UI… but if you already are in a background thread there is no real need to be asynchronous.

In fact if you start asynchronous network tasks you may cause an unintended threads proliferation and the process itself looses its “procedural” nature (which is the nature of most background processes). Indeed, to avoid such scenarios, you may write the entire flow inside the completion handlers of NSURLSessionDataTask’s delegates (in order to have only one active thread per process) but if you have multiple network requests to perform one after another this can lead to a very ugly (at leas in my opinion) competition-hendlerception (nested completion handlers).

In the same way if you are inside an NSOperation having synchronous network tasks really simplify your life for many reasons:

  • the code executed in the completion handler of an async task within an NSOperation is no more in the NSOperationQueue
  • the NSOperation may terminate before the actual task (what is performed by the completion handler provided) is finished, loosing the concurrency control provided by the NSOperationQueue

To address this problem I come up with a solution involving semaphores:

What I did here is to create a semaphore (using the GCD function dispatch_semaphore_create) with initial value equals to zero that per documentation is the value you would use to synchronise two threds:

Passing zero for the value is useful for when two threads need to reconcile the completion of a particular event

I passed the semaphore to the completion handler of the NSURLSessionDataTask which will release at its end (with dispatch_semaphore_signal) and after starting the task (calling its “resume” method) I just wait for the data task to signal the semaphore its completion (using dispatch_semaphore_wait).

With this technique I’m able to have a synchronised network task using asynchronous API.

Although working, the solution presented above can still be improved. In fact it requires the writing of too much boilerplate code more related to how I want the things to be done (synchronously) instead of what I want (the network task to be performed).
To address this “boilerplate code” problem Swift extensions are our friends! In fact extending NSURLSession adding a method that incorporates most of the above code will help us writing a much cleaner and elegant code:

With this nice extension all you need to launch a synchronous data task is:

Look how way better it is! And since I don’t like half-finished let’s create a nice method for creating an async request an call it “sendAsynchronousRequest“:

As you can see sendAsynchronousRequest returns the NSURLSessionDataTask right after starting it… this way it is possible to manipulate it or access its status.

Now you can perform perfectly synchronised network task whenever you want with a quick and clean way. I hope you enjoyed and found useful the post.

  • Jean Le Moignan

    AAAAARGH! Thanks a lot Stefano, you’ve just saved my life. I also noticed that NSURLSession was incompatible with NSOperation (the operation ending eons before anything be received from the server).

    Greetings from Quebec, Canada.

  • Элёржон Кимсанов

    Hi! Is there any way to avoid using URLSession and semaphores? Because using URLSession inside Operation leads to doubling threads count