Wrong order received by NSURLSession delegate?

Hi,


I'm using a NSURLSession with the backgroundSessionConfiguration to send a NSURLSessionUploadTask in the background.

I noticed that sometimes after waking by the OS in the app delegate's handleEventsForBackgroundURLSession, I get a call on the NSURLSessionDataDelegate for URLSession:dataTask:didReceiveData *directly before* URLSession:task:didCompleteWithError. They seem to be called by two different threads in the same time. Naturally, processing the didComplete before getting the data in didReceive results in bugs. Is this the normal behavior for background tasks and I should support these parallel out of order calls to the delegate for the same task, or is it a bug and just open a rdar?


Thanks

Accepted Reply

The problem here relates to how you construct

networkOperationQueue
. By default NSOperationQueue will return a concurrent queue, that is, a queue on which multiple operations can run concurrently. If you want the operations to be serialised, which is the only sane way to deal with NSURLSession callbacks, set the queue’s
maxConcurrentOperationCount
to 1.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

When you created the session, how do you set up the delegate queue?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

let conf = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("backgroundSession")

conf.discretionary = true;

conf.requestCachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData

conf.HTTPMaximumConnectionsPerHost = 4

networkOperationQueue = NSOperationQueue() // Member

networkSessionDelegate = CPURLProtocolSessionDelegate() // Member implementing NSURLSessionDataDelegate and NSURLSessionTaskDelegate interfaces

let session = NSURLSession(configuration: conf, delegate: self, delegateQueue: networkOperationQueue)

// And when sending requests we use

let task = session.uploadTaskWithRequests(request, fromFile:fileURL)

task.resume()

The delegate is called simultaneously with the didCompleteWithError and didReceiveData on two different threads.

The problem here relates to how you construct

networkOperationQueue
. By default NSOperationQueue will return a concurrent queue, that is, a queue on which multiple operations can run concurrently. If you want the operations to be serialised, which is the only sane way to deal with NSURLSession callbacks, set the queue’s
maxConcurrentOperationCount
to 1.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks!

Hi


What does the documentation for the delegateQueue parameter mean when it says:

"An operation queue for scheduling the delegate calls and completion handlers. The queue need not be a serial queue. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls."


It sounds like this is not correct?

It sounds like this is not correct?

I dunno, that sound correct to me. NSURLSession does not care if you use a serial queue or not; it works fine either way. If you’re prepared to handle callbacks out of order, you can use a concurrent queue if you want. That’s not a good idea IMO, but it’s your choice.

Regardless, if you feel that the documentation should be clearer on this point, feel free to file a bug describing your position. Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Our application has 7 api calls on home screen . We want the home screen to get loaded faster. We fire all 7 calls concurrently and not serially. We have to go with NSURLSessionDelegate as SSL pinning is implemented and hence we can not go with completion handler approach.
Setting maxConcurrentOperationCount to 1 would rather slow the home screen loading.
Is there any work around to get real time concurrency ? Will our app crash if we do not set maxConcurrentOperationCount to 1?
A. We have done these 2 things for order of callbacks ,
  1. We have maintained dictionary <taskId, data> format . This is the dictionary that gets filled during DidReceiveData (NSUrlSession session, NSUrlSessionDataTask dataTask, NSData data) callbacks . So the data gets filled in appropriately.

  2. We set TaskDescription on each task added to NSUrlSession. We set it with endpoint name.

string strTaskDescription = strEndPointName;
getRequestTask.TaskDescription = strTaskDescription;

So inside DidCompleteWithError(NSUrlSession session, NSUrlSessionTask task, NSError error) we again check the taskdescrition and come to know about the endpoint name. The data for taskidentifier( key) is also retrieved.

B. The only concern that we have is about usage of NSOperationQueue set for NSURLSession. If its maximumConcurrentOperations is not set to 1 then the dictionary (specified in pont A above) will get accessed by multiple concurrent tasks( operations) . Will its usage get synchronized automatically ?

I’ll respond on the specific thread you created for this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"