0
votes

I wonder whether it is possible to have multiple downloads when background fetch is happening (iOS7 Background Fetch). Currently I have an app which download data from around 6 RESTful api. These API calls use NSURLSession and download data parallelly. This works when the app is in foreground. I have implemented the background fetch feature with my application where app can update while it's in background. Unfortunately when background fetch is happening only the first API is calling six time. I really appreciate if anyone can help me on this.

This is the code for download tasks.

- (void)start {

    self.responseData = [NSMutableData data];
    self.isLoginRequest = YES;

    self.dateFormatter = [[NSDateFormatter alloc] init];
    [self.dateFormatter setDateStyle:NSDateFormatterMediumStyle];

    self.apiList = [NSMutableArray array];

    NSArray *apis = [self fetchAPIData];

    for (API *api in apis) {

        NSString *fullURL = [NSString stringWithFormat:@"%@/%@",BASE_URL, api.url];        

        switch ([api.type integerValue]) {
            case 1:
                [self.apiList addObject:[[BSJSONInfo alloc] initWithFileType:BSRequestLeadstatus andDownloadSource:fullURL]];
                break;
            case 2:
                [self.apiList addObject:[[BSJSONInfo alloc] initWithFileType:BSRequestParam andDownloadSource:fullURL]];
                break;
            case 3:
                [self.apiList addObject:[[BSJSONInfo alloc] initWithFileType:BSRequestLeadprofiles andDownloadSource:fullURL]];
                break;
            case 4:
                [self.apiList addObject:[[BSJSONInfo alloc] initWithFileType:BSRequestInstallations andDownloadSource:fullURL]];
                break;
            case 5:
                [self.apiList addObject:[[BSJSONInfo alloc] initWithFileType:BSRequestLeadproviders andDownloadSource:fullURL]];
                break;
            case 6:
                [self.apiList addObject:[[BSJSONInfo alloc] initWithFileType:BSRequestSaleorders andDownloadSource:fullURL]];
                break;                        
            default:
                break;
        }
    }     
    [self startAllDownloads:nil];    
}

- (void)startAllDownloads:(id)sender {

    self.session = [self backgroundSession];

    // Access all download info objects using a loop.
    for (int i=0; i<[self.apiList count]; i++) {
        BSJSONInfo *jsonInfo = [self.apiList objectAtIndex:i];

        // Check if a file is already being downloaded or not.
        if (!jsonInfo.isDownloading) {            
            if (jsonInfo.taskIdentifier == -1) {
                jsonInfo.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:jsonInfo.downloadSource]];
            }
            else{
                jsonInfo.downloadTask = [self.session downloadTaskWithResumeData:jsonInfo.taskResumeData];
            }

            jsonInfo.taskIdentifier = jsonInfo.downloadTask.taskIdentifier;                        
            [jsonInfo.downloadTask resume];            
            jsonInfo.isDownloading = YES;
        }
    }
}

-(int)getFileDownloadInfoIndexWithTaskIdentifier:(unsigned long)taskIdentifier{
    int index = 0;
    for (int i=0; i<[self.apiList count]; i++) {
        BSJSONInfo *jsonInfo = [self.apiList objectAtIndex:i];
        if (jsonInfo.taskIdentifier == taskIdentifier) {
            index = i;
            break;
        }
    }

    return index;
}

- (NSURLSession *)backgroundSession
{
    static NSURLSession *session = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:[NSString stringWithFormat:@"%@", SESSION_STRING]];
        configuration.timeoutIntervalForRequest = 30.0;
        configuration.timeoutIntervalForResource = 60.0;

        session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];

    });

    return  session;
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL
{    
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *URLs = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
    NSURL *documentsDirectory = [URLs objectAtIndex:0];
    NSURL *originalURL = [[downloadTask originalRequest] URL];
    NSURL *destinationURL = [documentsDirectory URLByAppendingPathComponent:[originalURL lastPathComponent]];
    NSError *errorCopy;

    [fileManager removeItemAtURL:destinationURL error:NULL];
    BOOL success = [fileManager copyItemAtURL:downloadURL toURL:destinationURL error:&errorCopy];

    if (success){        
        int index = [self getFileDownloadInfoIndexWithTaskIdentifier:downloadTask.taskIdentifier];
        BSJSONInfo *jsonInfo = [self.apiList objectAtIndex:index];                        
        jsonInfo.isDownloading = NO;
        jsonInfo.downloadComplete = YES;            
        jsonInfo.taskIdentifier = -1;            
        jsonInfo.taskResumeData = nil;            

//Let parser to parse the JSON
        dispatch_async(dispatch_get_main_queue(), ^{

            self.responseData = [NSData dataWithContentsOfURL:destinationURL];
            [self parsingJSONResponse:self.responseData withType:jsonInfo.type];
        });             
    }    
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    if (error != nil) {        
        self.networkError = error;
        [self.delegate downloadingError:[error localizedDescription]];

        dispatch_async(dispatch_get_main_queue(), ^{
            [self.delegate downloadingError:[NSString stringWithFormat:@"%@ for type %lu", [error localizedDescription], self.bsType]];
            return;
        });
    }    
    self.downloadTask = nil;    
}

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    BSAppDelegate *appDelegate = (BSAppDelegate *)[[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.backgroundSessionCompletionHandler != nil) {                
                void(^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;

                appDelegate.backgroundSessionCompletionHandler = nil;

                [[NSOperationQueue mainQueue] addOperationWithBlock:^{            
                    completionHandler();

                    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
                    localNotification.alertBody = @"All files have been downloaded!";
                    [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
                }];
            }
        }
    }];    
}

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{

//    BLog();
}

Thanks

1
I have been using NSURLSession in a production application and have been unable to have concurrent downloads in a background session. It would appear that while in a background thread the maximum http connection amount is ignored. If you would like to try anyways, set HTTPMaximumConnectionsPerHost on the NSURLSessionConfiguration object.Grant Amos
Thanks for the response. I have tried and no luck.Chinthaka

1 Answers

0
votes

Finally I found an answer by my self. If I use default delegate then the problem is solved. I afraid don't know the reason behind why the background fetch doesn't work with custom delegates when concurrently downloading.

    NSURLSession *session = [NSURLSession sharedSession];    
    for (int i=0; i<[self.apiList count]; i++) {
        BSJSONInfo *fdi = [self.apiList objectAtIndex:i];

        NSURLSessionTask *downloadTask = [session downloadTaskWithURL:[NSURL URLWithString:fdi.downloadSource] completionHandler:^(NSURL *url, NSURLResponse *response, NSError *error){

            NSData *d = [NSData dataWithContentsOfURL:url];
            NSDictionary *json = [NSJSONSerialization JSONObjectWithData:d options:0 error:nil];

        }];
        [downloadTask resume];
    }