0

I need to download a number of forms from web service.For that I have used NSOperationQueue like this

operationQueue = [NSOperationQueue new];
    for (int i=0;i<[tempArray count];i++) {
        CheckList * checklist = (CheckList *)[tempArray objectAtIndex:i];

        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                selector:@selector(downloadChecklistsInBackground:)
                                                                                  object:checklist];
        [operationQueue addOperation:operation];

The idea is , it should get executed without affecting the screen operations. And in the UI there will be separate download button for each of the forms.So if user clicks on any one of them, it should be downloaded immediately and it should be removed from the background process.The code is given below.

-(void)downloadChecklistsInBackground:(CheckList *)checklist
{
    BOOL isDone = NO;
    for (int j=0; j<[selectedArray count]; j++) {
        if([checklist.checklistId isEqualToString:[selectedArray objectAtIndex:j]])
        {
            isDone = YES;

        }
    }
    if(!isDone)
    {
        [backGroundQueueArr addObject:checklist];

        NSString * urlStr = [[BASE_URL stringByAppendingString:DOWNLOAD_CHECKLIST_URL] stringByAppendingFormat:@"%@/%d/%@/%@/%@",[ChecklistSingleton sharedSingleton].userSSO,[checklist.checklistId intValue],checklist.language,checklist.baseFormId,checklist.versionNo];
        NSURL * url = [NSURL URLWithString:urlStr];
        NSLog(@"url is %@",url);

        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        NSURLSession *session = [NSURLSession sessionWithConfiguration:config];

        NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:url];
        AppDelegate * appDelegateObj = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        [request setValue:[NSString stringWithFormat:@"Bearer %@",appDelegateObj.accessToken ]forHTTPHeaderField:@"Authorization"];
        [request setTimeoutInterval:240.0];
        [request setValue:@"Application/JSON" forHTTPHeaderField:@"Content-Type"];
        NSURLSessionDataTask * downloadTask =[session dataTaskWithRequest:request completionHandler:^(NSData * data, NSURLResponse * response, NSError * error)
                                              {
                                                  NSString * str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                                  NSLog(@"response is %@",str);
                                              }];
        [downloadTask resume];

    }


}
-(void)downloadCecklistWithChecklist:(CheckList *)check5
{

    [selectedArray addObject:check5];
    BOOL isDownloaded = NO;
    for (int j=0; j<[backGroundQueueArr count]; j++) {
        CheckList * checklistTobeChecked = [backGroundQueueArr objectAtIndex:j];
        if([checklistTobeChecked.checklistId isEqualToString:check5.checklistId])
        {
            isDownloaded = YES;
        }
    }
    if(!isDownloaded)
    {

    NSString * urlStr = [[BASE_URL stringByAppendingString:DOWNLOAD_CHECKLIST_METADATA_SUBURL] stringByAppendingFormat:@"%@/%d/%@/%@/%@",[ChecklistSingleton sharedSingleton].userSSO,[check5.checklistId intValue],check5.language,check5.baseFormId,check5.versionNo];
    NSURL * url = [NSURL URLWithString:urlStr];
    NSLog(@"url is %@",url);

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config];

    NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:url];
    AppDelegate * appDelegateObj = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    [request setValue:[NSString stringWithFormat:@"Bearer %@",appDelegateObj.accessToken ]forHTTPHeaderField:@"Authorization"];
    [request setTimeoutInterval:240.0];
    [request setValue:@"Application/JSON" forHTTPHeaderField:@"Content-Type"];
    NSURLSessionDataTask * downloadTask =[session dataTaskWithRequest:request completionHandler:^(NSData * data, NSURLResponse * response, NSError * error)
                                          {
                                              NSString * str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                              NSLog(@"response is specific %@",str);
                                          }];
    [downloadTask resume];
    }


}

Could you please help me with the correct approach for this.I know that, this piece of code doesn't implement threading properly. Please guide me on this.Thank you very much in advance.

vadian
  • 274,689
  • 30
  • 353
  • 361
iOSManiac
  • 139
  • 1
  • 11

1 Answers1

0

NSURLSession is already wraps the async download operation in this case you don't have to call it from NSInvocationOperation. I would suggest you to :

1) init your buttons tag with the index of each checklist index

2) hold NSMutableIndexSet of downloaded check lists

3) disable the clicked button while its downloading

4) add the index of downloaded checklist

5) check if all list were downloaded if yes remove all from tempArray

6) enable the button

create a general event for all buttons like:

-(IBAction)myButtonClickEvent:(id)sender{

  // 2 - protects your data from being downloaded twice
  if([indexSet contains:sender.tag])
     return;
  // 3 - disable while downloading
  sender.enabled = NO;
  CheckList * check5 = [tempArray objectAtIndex : sender.tag];
  NSString * urlStr = [[BASE_URLstringByAppendingString:DOWNLOAD_CHECKLIST_METADATA_SUBURL] stringByAppendingFormat:@"%@/%d/%@/%@/%@",[ChecklistSingleton sharedSingleton].userSSO,[check5.checklistId intValue],check5.language,check5.baseFormId,check5.versionNo];
  NSURL * url = [NSURL URLWithString:urlStr];
  NSLog(@"url is %@",url);
  NSURLSessionConfiguration *config = [NSURLSessionConfiguration  defaultSessionConfiguration];
  NSURLSession *session = [NSURLSession sessionWithConfiguration:config];

  NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:url];
AppDelegate * appDelegateObj = (AppDelegate *)[[UIApplication sharedApplication] delegate];
 [request setValue:[NSString stringWithFormat:@"Bearer %@",appDelegateObj.accessToken ]forHTTPHeaderField:@"Authorization"];
 [request setTimeoutInterval:240.0];
 [request setValue:@"Application/JSON" forHTTPHeaderField:@"Content-Type"];
NSURLSessionDataTask * downloadTask =[session dataTaskWithRequest:request completionHandler:^(NSData * data, NSURLResponse * response, NSError * error)
                                      {
                                          // 4 - add index of downloaded checklist
                                          [indexSet addIndex:sender.tag];
                                          //5 - remove all checklists if all of them are downloaded
                                          if([indexSet count]==[tempArray count])
                                                 [tempArray removeAllObjects];
                                          NSString * str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                                          NSLog(@"response is specific %@",str);

                                          //6 - enable the button
                                          sender.enabled = YES;

                                      }];
[downloadTask resume];

}
Marat Ibragimov
  • 1,076
  • 7
  • 7
  • Use category to attach your model to UIButton http://muhammadzahidimran.com/2016/12/09/adding-data-to-sdk-classes-using-categories/ – Zahid Dec 14 '16 at 14:59
  • @Marat Ibragimov : Could you explain more on the background download of all checklists. Also can we add random numbers like checklistIDs as indexes in NSMutableIndexSet?Thank you very much for your response – iOSManiac Dec 15 '16 at 07:00
  • I added only the part of the button click, I'll add the background part to my answer. the indexes that I save are for checking if the checklist was downloaded, the button has tag property which holds the index of each check list, since I assume you don't change tempArray during the download process.once you have a response from the url session you and there is no error you save the index into NSMutableIndexSet (only the index of checklist in its tempArray) – Marat Ibragimov Dec 15 '16 at 08:45
  • regarding the downloads that runs without the button trigger, you fire them all at once, they don't have any dependencies on each other, they can run non serial , so triggering a download by a click could not have any effect on the downloading,because it might already in download proccess.you can make dependencies between the operations fire them and if you click on the button it cancels the operation that related to this download task (if it still not runs) and download it with regular NSURLSession task – Marat Ibragimov Dec 15 '16 at 09:59
  • @Marat Ibragimov: So there is no need to use NSOperationQueue to run it in a background thread?(for download all operation) – iOSManiac Dec 15 '16 at 10:35
  • 1
    NSUrlSession already provides you async networking calls,but if you need more control on you networking task , adding dependencies between the calls it can be done with NSOperationQueue operations but it will require a little bit more work to do , see this example of using NSOperation with NSURLSession api: http://stackoverflow.com/questions/21198404/nsurlsession-with-nsblockoperation-and-queues – Marat Ibragimov Dec 15 '16 at 10:53