Zohar81
Zohar81

Reputation: 5074

NSURLSession: method dataTaskWithRequest never reach completion callback on lengthy responses

The following code that used to create communication session with a remote server and send/receive HTTP requests/responses.

However, when a large file is attached to the response, the callback block never reached.

Only when explicitly invoke the cancel method after some timeout from the NSURLSession task (_dataTask), this callback is being called.

notice that using tcpdump it can easily observed that the response was properly received on the client side.

NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;

NSURLSession* session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:queue];

_dataTask = [session dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if ([error code] == NSURLErrorCancelled) {
        writeLog(LOG_ERROR, "NSURLErrorCancelled");
    } else {
        ...
    }
}];

[_dataTask resume]

// after timeout, the operation is cancelled.
sleep(100)
[_dataTask cancel];

I'd like to know if using dataTask has response length limit (because it's working for small files on response body) and if there is such a limit, so which other method should I use in order to overcome it.

I saw that there's an alternative method in NSUrlsession dedicated for downloading files called downloadTaskWithRequest but it doesn't have an async completion block.

Thanks !

Upvotes: 0

Views: 496

Answers (1)

Rob
Rob

Reputation: 437552

When fetching large resources, you should use download task. A data task will attempt to load the entire response in a single NSData object. Loading a large asset in memory at the same time is not only inefficient, but if it is extraordinarily large, can cause problems.

A download task is well suited for these tasks, because it will stream the asset to a temporary file for you, reducing the peak memory usage. (Admittedly, you can manually achieve the same with data task with delegate pattern, but download tasks do this for you.)

You said:

I saw that there's an alternative method in NSURLSession dedicated for downloading files called downloadTaskWithRequest but it doesn't have an async completion block.

Two observations:

  1. There is a rendition, dataTaskWithRequest:completionHandler:, that has a completion block:

    NSURLSession* session = [NSURLSession sharedSession];
    NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        ...
    }];
    [task resume];
    

    Note, I would advise using sharedSession if you are not setting a delegate, or otherwise customizing your NSURLSession. You do not want to instantiate NSURLSession objects unnecessarily. And if you really must instantiate a NSURLSession, re-use it for subsequent tasks and/or make sure to call finishTasksAndInvalidate after submitting the last task for that session, or else the NSURLSession will leak. And, if you instantiate your own NSURLSession, you do not have to instantiate your own operation queue, as it will, by default, create a serial queue for you if you do not supply an operation queue.

  2. The rendition without a block parameter, downloadTaskWithURL:, works, too. All you need to do is to specify a delegate for your NSURLSession and then and implement URLSession:downloadTask:didFinishDownloadingToURL:.

    The reason I suggest this is that, often, when we are downloading very large assets (especially over cellular), we realize that the users may want to leave our app and let the download complete in the background. In those situations, we would use a background NSURLSessionConfiguration. And when using background sessions, you must use this delegate-based approach. So, if you think you might eventually adopt background sessions for long downloads, then adopting a delegate-based approach now is not a bad idea.

    For more information, see Downloading Files in the Background.

Upvotes: 2

Related Questions