Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AFNetworking 2.0.0 download in background

I'm using 'AFNetworking', '2.0.0' for download data.

I need to download large file. When user lock screen or press home button it should go pause (or continue downloading in background) and if I return to app it should resume.

Also I need to show progress of downloading.

I search a lot of examples but don't find anything 'AFNetworking', '2.0.0'.

I create app for iOS version 6.0+, so I can't use AFHTTPSessionManager or AFURLSessionManager.

like image 359
user3414679 Avatar asked Mar 13 '14 09:03

user3414679


1 Answers

For downloading in background on iOS 7 or higher i'am using NSURLSession with their NSURLSessionDownloadTask.

Switch on BackgroundMode in ProjectNavigator->YourProject->YourTarget->Capabilities (tab)->Background Modes enter image description here

Add to your AppDelegate method - (BOOL)application:(UIApplication* )application didFinishLaunchingWithOptions:(NSDictionary* )launchOptions an initialiser for NSURLSessin with next code:

    NSURLSessionConfiguration *sessionConfiguration;
    NSString *someUniqieIdentifierForSession = @"com.etbook.background.DownloadManager";
    if ([[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."] firstObject] integerValue] >= 8) {
//This code for iOS 8 and greater
        sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:someUniqieIdentifierForSession];
    } else {
this code for iOS 7
        sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:someUniqieIdentifierForSession];
    }
    sessionConfiguration.HTTPMaximumConnectionsPerHost = 5;
    self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                 delegate:self
                                            delegateQueue:[NSOperationQueue mainQueue]];

Of course, don't forget to declare session property with:

@property (nonatomic, strong) NSURLSession session;

Also add completion handler property (it will be needed if you want to get callback of background download process after Application termination or crash):

@property (nonatomic, copy) void(^backgroundTransferCompletionHandler)();

Add delegate methods in AppDelegate:

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{

    NSError *error;
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSString *destinationFilename = downloadTask.originalRequest.URL.lastPathComponent;
    NSURL *destinationURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:destinationFilename];

    if ([fileManager fileExistsAtPath:[destinationURL path]]) {
        [fileManager removeItemAtURL:destinationURL error:nil];
    }
    [fileManager copyItemAtURL:location
                         toURL:destinationURL
                         error:&error];
}

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    if (error != nil) {
        NSLog(@"Download completed with error: %@", [error localizedDescription]);
    }
    else{
        NSLog(@"Download finished successfully.");
}

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{

    if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) {
        NSLog(@"Unknown transfer size");
    }
    else{
        NSLog(@"progress = %lld Mb of %lld Mb", totalBytesWritten/1024/1024, totalBytesExpectedToWrite/1024/1024);
    }
}

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{
    ETBAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;

    // Check if all download tasks have been finished.
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {

        if ([downloadTasks count] == 0) {
            if (appDelegate.backgroundTransferCompletionHandler != nil) {
                // Copy locally the completion handler.
                void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;

                // Make nil the backgroundTransferCompletionHandler.
                appDelegate.backgroundTransferCompletionHandler = nil;

                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // Call the completion handler to tell the system that there are no other background transfers.
                    completionHandler();

                    // Show a local notification when all downloads are over.
                    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
                    localNotification.alertBody = @"All files have been downloaded!";
                    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
                }];
            }
        }
    }];
}


//Background download support (THIS IMPORTANT METHOD APPLICABLE ONLY IN YOUR AppDelegate.m FILE!!!)
-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{
    self.backgroundTransferCompletionHandler = completionHandler;
}

And declare that your class (in AppDelegate.h file for my example) conforms to protocol NSURLSessionDelegate like here:

@interface AppDelegate : UIResponder <UIApplicationDelegate, NSURLSessionDelegate>

Then add download task somewhere with:

NSURLSessionDownloadTask *task = [self.session downloadTaskWithURL:[NSURL URLWithString:urlString]];
task.taskDescription = [NSString stringWithFormat:@"Downloading file %@", [urlString lastPathComponent]];
[task resume];

So when your application started after termination, your session will be restored and delegate methods will be fired. Also if your application will waked up from background to the foreground your application delegate methods will be fired too.

like image 67
fir Avatar answered Sep 28 '22 02:09

fir