I have used NSURLSession to download a single file and it is working fine,Now i have to download three files in background and also have to manage their progress in UIProgress.My code for single downloading is below..
- (IBAction)startBackground:(id)sender
{
// Image CreativeCommons courtesy of flickr.com/charliematters
NSString *url = @"http://farm3.staticflickr.com/2831/9823890176_82b4165653_b_d.jpg";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
self.backgroundTask = [self.backgroundSession downloadTaskWithRequest:request];
[self setDownloadButtonsAsEnabled:NO];
self.imageView.hidden = YES;
// Start the download
[self.backgroundTask resume];
}
- (NSURLSession *)backgroundSession
{
static NSURLSession *backgroundSession = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.shinobicontrols.BackgroundDownload.BackgroundSession"];
backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
});
return backgroundSession;
}
#pragma mark - NSURLSessionDownloadDelegate methods
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
double currentProgress = totalBytesWritten / (double)totalBytesExpectedToWrite;
dispatch_async(dispatch_get_main_queue(), ^{
self.progressIndicator.hidden = NO;
self.progressIndicator.progress = currentProgress;
});
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
// Leave this for now
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
// We've successfully finished the download. Let's save the file
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
NSURL *documentsDirectory = URLs[0];
NSURL *destinationPath = [documentsDirectory URLByAppendingPathComponent:[location lastPathComponent]];
NSError *error;
// Make sure we overwrite anything that's already there
[fileManager removeItemAtURL:destinationPath error:NULL];
BOOL success = [fileManager copyItemAtURL:location toURL:destinationPath error:&error];
if (success)
{
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithContentsOfFile:[destinationPath path]];
self.imageView.image = image;
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.hidden = NO;
});
}
else
{
NSLog(@"Couldn't copy the downloaded file");
}
if(downloadTask == cancellableTask) {
cancellableTask = nil;
} else if (downloadTask == self.resumableTask) {
self.resumableTask = nil;
partialDownload = nil;
} else if (session == self.backgroundSession) {
self.backgroundTask = nil;
// Get hold of the app delegate
SCAppDelegate *appDelegate = (SCAppDelegate *)[[UIApplication sharedApplication] delegate];
if(appDelegate.backgroundURLSessionCompletionHandler) {
// Need to copy the completion handler
void (^handler)() = appDelegate.backgroundURLSessionCompletionHandler;
appDelegate.backgroundURLSessionCompletionHandler = nil;
handler();
}
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
dispatch_async(dispatch_get_main_queue(), ^{
self.progressIndicator.hidden = YES;
[self setDownloadButtonsAsEnabled:YES];
});
}
You can have multiple NSURLSessionDownloadTask use the same NSSession and each are executed one after the other on the same mainQueue.
when successfully downloaded they call:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
If you download a MP4 of 100 mB and a pic of 10KB then they will return in different orders into this method.
So to track what session and what downloadTask has returned in this
you need to set a string identifier BEFORE you call the web service
To distinguish/track NSURLSessions you set
session.configuration.identifier
to distinguish NSURLSessionDownloadTask use
downloadTask_.taskDescription
downloadTask_.taskDescription = [NSString stringWithFormat:@"%@",urlSessionConfigurationBACKGROUND_.identifier];
For example in my project I was downloading a number of user favourite videos and their thumbnails.
Each item had a id e.g.1234567 So I needed to make two calls for each favourite
so i created two identifiers
"1234567_VIDEO"
"1234567_IMAGE"
then called two ws calls and passed in the identifier in session.configuration.identifier
http://my.site/getvideo/1234567
"1234567_VIDEO"
http://my.site1/getimage/1234567
"1234567_IMAGE"
iOS7 will download the items in the background, app can go back to sleep When done it calls
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
I then get
session.configuration.identifier
"1234567_IMAGE"
split it up and check the values
1234567_IMAGE
"1234567"
"_IMAGE" > item at location is a MP4 so save as /Documents/1234567.mp4
"_VIDEO" > item at location is a jpg so save as /Documents/1234567.jpg
If you have 3 urls to call you can have One NSURLSessionDownloadTask per NSSession
file 1 - NSSession1 > NSURLSessionDownloadTask1
file 2 - NSSession2 > NSURLSessionDownloadTask2
file 3 - NSSession3 > NSURLSessionDownloadTask3
This seems to work fine when the app is in the foreground. But I had problems when using BACKGROUND TRANSFER with BACKGROUND FETCH. The first NSSession > NSURLSessionDownloadTask1 would return and then none of the others would be called.
So safer to have multiple NSURLSessionDownloadTask in one NSSession1
file 1 - NSSession1 > NSURLSessionDownloadTask1
file 2 - > NSURLSessionDownloadTask2
file 3 - > NSURLSessionDownloadTask3
Be careful when doing this call NSSession finishTasksAndInvalidate not invalidateAndCancel
//[session invalidateAndCancel];
[session finishTasksAndInvalidate];
invalidateAndCancel will stop the session and not finish the other download tasks
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