Home NSURLSessionDownloadTask occasionally results in nil data
Reply: 1

NSURLSessionDownloadTask occasionally results in nil data

inorganik
1#
inorganik Published in 2018-01-11 22:50:01Z

I'm downloading images asynchronously from a CDN in my app (In a UICollectionView). Each time I run it, different images will fail to load. About 1-3 out of 22. Sometimes (rarely) they all load. But the point is it's not consistent. What's happening is that in this line:

NSData *fileData = [NSData dataWithContentsOfURL:location];

fileData is intermittently nil . Strangely, error from the NSURLSessionDownloadTask is also nil. Here's the complete method:

+ (void) downloadFileAsynchronouslyWithUrl:(NSURL *)fileUrl andCallback:(void (^)(NSData* fileData, NSError* error))callback {
    NSURLSessionDownloadTask *downloadTask = [[NSURLSession sharedSession] downloadTaskWithURL:fileUrl completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error != nil) {
                NSLog(@"ERROR: %@", error.localizedDescription);
                callback(nil, error);
            }
            else {
                NSData *fileData = [NSData dataWithContentsOfURL:location];
                if (fileData != nil) {
                    callback(fileData, nil);
                }
                else {
                    // Getting this intermittently
                    NSError *err = [self errorFromString:@"downloaded file was nil!"];
                    callback(nil, err);
                }
            }
        });
    }];
    [downloadTask resume];
}

I have logged the status code, and it's always 200.

It is baffling me what could cause this. Any ideas?

Rob
2#
Rob Reply to 2018-01-11 23:06:54Z

You should make sure to move the file to something local (or load it into NSData) synchronously. When you return from this downloadTaskWithURL completion block, the file is removed. And you're attempting to read this file from within dispatch_async, which introduces a race condition between the grabbing of the data from the file and the OS removing this temporary file for you.

So, you might try something like:

+ (void) downloadFileAsynchronouslyWithUrl:(NSURL *)fileUrl andCallback:(void (^)(NSData* fileData, NSError* error))callback {
    NSURLSessionDownloadTask *downloadTask = [[NSURLSession sharedSession] downloadTaskWithURL:fileUrl completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error != nil) {
            NSLog(@"ERROR: %@", error.localizedDescription);
            dispatch_async(dispatch_get_main_queue(), ^{
                callback(nil, error);
            });
        }
        else {
            NSData *fileData = [NSData dataWithContentsOfURL:location];
            dispatch_async(dispatch_get_main_queue(), ^{
                if (fileData != nil) {
                    callback(fileData, nil);
                }
                else {
                    // Getting this intermittently
                    NSError *err = [self errorFromString:@"downloaded file was nil!"];
                    callback(nil, err);
                }
            });
        }
    }];
    [downloadTask resume];
}

Alternatively, you could consider using a URLSessionDataTask, which avoids this issue. We usually use download tasks when we're trying to reduce peak memory usage and/or using background sessions, but neither of those situations apply here.

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.34997 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO