When is the .Completed
state set on NSURLSessionTask
and how does it depend/affect the completionHandler
of the same task?
Is there a way to make sure that the .Completed
state is only set after the completionHandler
is finished executing?
Following another question on here... Chaining multiple async functions in Swift
I was pointed in the direction of the Advanced NSOperations WWDC Talk and Sample Code.
After replicating part of the code into my own project I've found that I seem to encounter a race condition that will sometimes work and sometimes fail depending on the way the race condition plays out.
The operation I have created is pretty much a copy of the DownloadEarthquakesOperation
in the sample code.
It is a subclass of GroupOperation
and contains a URLSessionTaskOperation
. The NSURLSessionTask
is created with a completionHandler
which processes the downloaded data.
The class URLSessionTaskOperation
works by observing the state
property of its task and then finishing the operation when it is changed to .Completed
.
The problem I am encountering is that it seems the state
of the task is changed to .Completed
before the completionHandler
has finished processing.
The completion handler I have is like this...
// this is a direct copy of the sample code just using data task
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
self.downloadFinished(data, response: response as? NSHTTPURLResponse, error: error)
}
The function it calls (in pseudo code) is something like this... (I don't have access to the exact code at the moment).
func downloadFinished(data: NSData?, response: NSHTTPURLResponse?, error, NSError?) {
// if the error is not nil then finish operation with error
// if the response status code is not correct then finish with error
// try to convert the data to a JSON object using NSJSONSerialization
// finish with error if conversion failed
// get a single Int value out of the JSON object
// store the single Int value in an instance variable
}
There is no asynchronous code here.
After this has completed the part operation of this (equivalent to the GetEarthquakesOperation
in the sample code) gets the value from the instance variable and passes it into the next operation.
The problem is that sometimes this value is there and sometimes it is nil.
By logging out various lines in the different classes I can see that the network operation is set as completed at some point along the execution of the completion handler. Sometimes before the value is set and sometimes after.
What's baffling is that I have tried forcing this to happen in the sample app but I couldn't. I have tried sleep
ing the completion handler and I've tried putting in a long execution time loop but neither of them seem to work.
Can anyone help me in trying to fix this race condition.
OK, this is bizarre.
http://www.oliverfoggin.com/advanced-nsoperations-nsurlsessiondatatask-vs-nsurlsessiondownloadtask/
NSURLSessionDataTask and NSURLSessionDownloadTask both run their completion handlers and set their completed state in different ways.
The download task only sets itself to completed after the completion handler has finished executing.
The data task sets itself to completed after the completion handler has started executing.
This is causing the race condition in my own project. I think I'll switch to using the download task for now.
I'll be filing a radar also.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With