Jackky White
Jackky White

Reputation: 357

NSURLSession with DataTask Delegate return expectedContentLength negative(-1) , Cant calculate file size

I'm trying to calculate the data that is being received in dataTaskWithURL for a progress bar using this follow up delegate method: NSURLSessionTaskDelegate, NSURLSessionDataDelegate, this is the code I use in the view didload, I'm not using request. web adress : http://www.roimulia.com/db.php Global properties :

var dataTodownload : NSMutableData!
var downloadSize : Float!

ViewDidload :

var defaultConfigObject : NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
var  defaultSession  : NSURLSession = NSURLSession(configuration: defaultConfigObject, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
let url = NSURL(string: "http://www.roimulia.com/db.php")
var datatask = defaultSession.dataTaskWithURL(url!)
datatask.resume()

Relevant Delegate methods :

   func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void)
    {
        completionHandler(NSURLSessionResponseDisposition.Allow)
        prgs_bar.progress = 0
        downloadSize = Float(response.expectedContentLength)
        dataTodownload = NSMutableData()           
    }

    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData)
    {
        dataTodownload.appendData(data)
        prgs_bar.progress = Float(dataTodownload.length) / Float(downloadSize)
    }

After debugging I found out that the progress bar doesn't load, even if the data was received completely. The reason it's happening is because the value of response.expectedContentLength equals -1, which means something about the default value of the length when it the file on the server is compressed.

==============================

UPDATE BY ROB ANSWER :

==============================

So as a start, forget what i have commented below , i missed something you wrote. Rob suggested 3 answers. As i see it, answer number 2 is the one i'm looking for, Because it uses delegate method, and because im planning to go big with this app, I need to make it as efficient as i can (Unless and i'm wrong, please refer to this line too, I really care about efficiency of Download/receive data from the web). So i found out 2 interesting things : 1)When i use a file as the url in the request that locate on my server and parsed as json - as for http://roimulia.com/db.php it does receive the data at didCompleteWithError delegate method, and i can use NSJSONSerialization.JSONObjectWithData to get the data out. BUT the expectedContentLength equals -1 , so i cant calculate it progress. But when i use this link for exmaple(not locate on my server) http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo - It loads with perfectly) , expectedContentLength is not eqal -1 , and i can calaculate it progress . So conclusion one : 1) The problem might be connected to my server or to the way i echo my Array at the php file(although i can use the data at the end so it's weird). Let me know what you think about that) 2) If we couldnt solve it via solution number 2 :

1)How much the two others solutions (1 , 3) are less efficient (as for download speed , progress, etc) compare to solution 2, If any?

I hope it detail enough for now :X I cant wait to solve it

Upvotes: 1

Views: 2839

Answers (1)

Rob
Rob

Reputation: 438307

Correct. You do not have expected content length if it is compressed.

  1. You can turn off compression by setting Accept-Encoding header to identity:

    let url = NSURL(string: "http://www.roimulia.com/db.php")
    let request = NSMutableURLRequest(URL: url!)
    request.setValue("identity", forHTTPHeaderField: "Accept-Encoding")
    
    let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    let session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
    
    let task = session.dataTaskWithRequest(request)
    task.resume()
    

    It should be noted that, even then, you might not get a valid expectedContentLength (e.g. chunked response of indeterminate length), but generally it will work.

  2. Alternatively, rather than slowing down the download for the sake of a progress view, you might want to show something that moves as data is received, letting the user know that there is actual data being received, but have it loop:

    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
        dataTodownload.appendData(data)
    
        var progress: Float!
    
        if downloadSize < 0 {
            progress = (Float(dataTodownload.length) % 10_000_000.0) / 10_000_000.0
        } else {
            progress = Float(dataTodownload.length) / downloadSize
        }
    
        progressView.progress = progress
    }
    
  3. Since you have a PHP web service delivering this download, you theoretically could have your server determine what the uncompressed size of the download was going to be, and include that in the response somehow (custom header, some other API call that returns it, etc.).

Upvotes: 1

Related Questions