NSURLSession & synchronous requests

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’sdelegates (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 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:

Synchronous NSULRSessionTask V1

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

leturl=NSURL(string:"https://api-url.com")!

letrequest=NSMutableURLRequest(URL:url)

letsession=NSURLSession.sharedSession()

letsemaphore=dispatch_semaphore_create(0)

lettask=session.dataTaskWithRequest(request){data,response,error in

ifdata=data{

print(String(data:data,encoding:NSUTF8StringEncoding))

}else{

print(error)

}

dispatch_semaphore_signal(semaphore)

}

task.resume()

dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER)

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: