NSURLSession backgroundSession допълнително извикване на API при завършване

Трябва да кача съдържание в AWS S3 чрез PUT, което може да се изпълнява във фонова сесия с помощта на NSURLSessionUploadTask.

Засега работи чудесно. След това обаче трябва да извикам моя API, след като качването в S3 приключи, за да променя състоянието му, за да завърши.

Аз AWSS3, за да създам заявката S3 и след това я копирам в NSURLSessionUploadTask съгласно това ТАКА отговор. Това работи както на преден план, така и на заден план и качва файла добре в S3.

Сега това е частта, за която имам нужда от помощ. Опитах се да използвам както URLSession:task:didCompleteWithError, така и URLSessionDidFinishEventsForBackgroundURLSession делегатни методи, за да извикам моята допълнителна заявка за API, използвах както стандартни задачи за данни, така и задачи за изтегляне, те не изглежда да задействат заявката във фонов режим, докато Отварям приложението отново. В идеалния случай бих искал да стрелят веднага. Те правят пожар на качването е на преден план. Виждал съм Wunderlist да прави точно това, което се опитвам да направя във фонов режим, но не знам как го правят.

Ето какво имам досега ... Всяка помощ/предложения биха били невероятни, това ме подлудява! :)

- (IBAction)upload:(id)sender {
  AmazonS3Client *s3Client = [[AmazonS3Client alloc] initWithAccessKey:@"ABC" withSecretKey:@"123"];

  NSString *identifier = [NSString stringWithFormat:@"com.journeyhq.backgroundSession.%@-%@", @"S3", @"ATTACHMENT_REMOTE_ID"];
  NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
  NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];

  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  NSString *documentsDirectory = [paths objectAtIndex:0];
  NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"img.jpg"];
  NSURL *fromFile = [NSURL fileURLWithPath:filePath isDirectory:NO];

  S3PutObjectRequest *s3Request = [[S3PutObjectRequest alloc] initWithKey:[[NSString stringWithFormat:@"%@__%@__%@", @"ATTACHMENT_ID", @"ATTACHMENT_UUID", @"img.jpg"] lowercaseString] inBucket:@"BUCKET_NAME"];
  s3Request.cannedACL = [S3CannedACL publicReadWrite];

  NSMutableURLRequest *request = [s3Client signS3Request:s3Request];

  NSString *urlString = [NSString stringWithFormat:@"https://%@.%@/%@", @"BUCKET_NAME", @"s3-eu-west-1.amazonaws.com", [[NSString stringWithFormat:@"%@__%@__%@", @"ATTACHMENT_ID", @"ATTACHMENT_UUID", @"img.jpg"] lowercaseString]];
  request.URL = [NSURL URLWithString:urlString];

  NSMutableURLRequest *request2 = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]];
  [request2 setHTTPMethod:@"PUT"];
  [request2 setAllHTTPHeaderFields:[request allHTTPHeaderFields]];
  [request2 setValue:@"BUCKET_NAME.s3-eu-west-1.amazonaws.com" forHTTPHeaderField:@"Host"];

  NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request2 fromFile:fromFile];
  [uploadTask resume];
}

#pragma - NSURLSessionTaskDelegate

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
    dispatch_async(dispatch_get_main_queue(), ^{
      self.progressView.progress = (float)totalBytesSent / (float) totalBytesExpectedToSend;
    });
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
  NSURLSessionConfiguration *sessionConfig = [session configuration];
  NSString *identifier = [sessionConfig identifier];

  NSLog(@"**identifier**: %@", identifier);

  NSArray *identifierComponents = [identifier componentsSeparatedByString:@"."];
  NSString *lastComponent = [identifierComponents lastObject];
  NSArray *components = [lastComponent componentsSeparatedByString:@"-"];
  NSString *sessionType = [components firstObject];
  NSString *attachmentID = [components lastObject];

  NSLog(@"sessionType: %@", sessionType);
  NSLog(@"attachmentID: %@", attachmentID);

  if (error == nil)
  {
    NSLog(@"Task %@ completed successfully", task);
    if ([sessionType isEqualToString:@"S3"]) {
        NSString *downloadIdentifier = [NSString stringWithFormat:@"com.journeyhq.backgroundSession.%@-%@", @"s3Completion", attachmentID];
        NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:downloadIdentifier];
        NSURLSession *downloadSession = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];

        NSString *urlString = @"API_COMPLETION_URL";
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]];

        NSURLSessionDownloadTask *downloadTask = [downloadSession downloadTaskWithRequest:request];
        [downloadTask resume];
    }
  }
  else
  {
    NSLog(@"Task %@ completed with error: %@", task,
          [error localizedDescription]);
  }
  task = nil;

}

AppDelegate.h

- (void)application:(UIApplication *)application
handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)())completionHandler {
}

РЕДАКТИРАНЕ Опитах и ​​следното. Задачата се създава както преди, все още не се задейства, когато възобновя задачата.

- (void)application:(UIApplication *)application
handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)())completionHandler {

    NSDictionary *userInfo = @{
        @"completionHandler" : completionHandler,
        @"sessionIdentifier" : identifier
    };

    [[NSNotificationCenter defaultCenter]
        postNotificationName:@"BackgroundTransferNotification"
        object:nil
        userInfo:userInfo];
}

След това в контролера за изглед

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    [[NSNotificationCenter defaultCenter]
     addObserver:self
     selector:@selector(handleBackgroundTransfer:)
     name:@"BackgroundTransferNotification"
     object:nil];
}

- (void)handleBackgroundTransfer:(NSNotification *)notification {
    // handle the API call as shown in original example
    ...
}

person Steve P. Sharpe    schedule 07.04.2014    source източник


Отговори (2)


Както се казва в документацията за NSURLSessionDidFinishEventsForBackgroundURLSession:

В iOS, когато фоновото прехвърляне завърши или изисква идентификационни данни, ако приложението ви вече не работи, приложението ви автоматично се стартира отново във фонов режим и на UIApplicationDelegate на приложението се изпраща съобщение application:handleEventsForBackgroundURLSession:completionHandler:. Това извикване съдържа идентификатора на сесията, която е причинила стартирането на вашето приложение. След това вашето приложение трябва да съхрани този манипулатор на завършване, преди да създаде фонов конфигурационен обект със същия идентификатор и да създаде сесия с тази конфигурация. Новосъздадената сесия автоматично се свързва отново с текущата фонова дейност.

Когато приложението ви по-късно получи съобщение URLSessionDidFinishEventsForBackgroundURLSession:, това показва, че всички съобщения, поставени преди това в опашката за тази сесия, са доставени и че вече е безопасно да извикате предварително съхранения манипулатор за завършване или да започнете всякакви вътрешни актуализации, които могат да доведат до извикване на манипулатора за завършване .

Така че handleEventsForBackgroundURLSession трябва да запази completionHandler и да възстанови фона NSURLSession. Тогава URLSessionDidFinishEventsForBackgroundURLSession трябва да нарече това completionHandler.

Не виждам да създавате отново фоновата сесия никъде, когато приложението ви се събуди отново и се извика handleEventsForBackgroundURLSession. Нито пък виждам да прилагате URLSessionDidFinishEventsForBackgroundURLSession, който ще извика това completionHandler.

person Rob    schedule 30.10.2014

Ако фонова задача е завършена, докато приложението ви не работи, ще трябва да я обработите във вашия AppDelegate, което изглежда не правите.

Нещо подобно трябва да работи:

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {

    // the identifier you used to create the background session
    NSString *identifier = [NSString stringWithFormat:@"com.journeyhq.backgroundSession.%@-%@", @"S3", @"ATTACHMENT_REMOTE_ID"];

    if ([identifier isEqualToString:identifier]) {
        // call your API here
    }

    // call the handler block so the app can exit again
    completionHandler();    
}
person Hugo Sousa    schedule 07.04.2014
comment
Благодаря за отговора Хюго. Опитах това преди, макар и не директно в делегата на приложението, а чрез известие, задействано вътре в handleEventsForBackgroundURLSession, което след това се обработва в контролер за изглед. За съжаление резултатът беше същият. Създава задачата, но просто не се задейства, въпреки че извиквам възобновяване на задачата, освен ако не отворя приложението, тогава задачата се изпълнява. Опитах го директно в handleEventsForBackgroundURLSession и все още не се задейства. - person Steve P. Sharpe; 07.04.2014
comment
Можете ли да публикувате кода, който опитахте в метода за делегиране на приложението? - person Hugo Sousa; 10.04.2014
comment
Решавал ли си някога това? Имам точно същия проблем - person d0n13; 03.03.2016