Я пытаюсь реализовать фоновую выборку в приложении iOS. В официальной документации указано:
"Вам не обязательно выполнять всю фоновую сетевую активность с помощью фоновых сеансов... .... Приложения, объявляющие соответствующие фоновые режимы, могут использовать сеансы URL-адресов по умолчанию и задачи данных, как если бы они были на переднем плане".
Более того, в этом видео WWDC 2014 на 51:50 между лучшими практиками говорится:
"Одна из распространенных ошибок, которую, как нам кажется, совершали некоторые люди в прошлом, заключается в том, что они предполагают, что при работе в фоновом режиме для обработки таких вещей, как обновление фоновой выборки... ... они должны были использовать фоновый сеанс. Теперь, использование фоновой загрузки или загрузки... ...работает очень хорошо, но особенно для больших загрузок. приложение приостанавливается] «Если у вас есть такая же небольшая сетевая задача, которая может быть завершена в течение этого времени, вполне нормально сделать это в процессе или по умолчанию NSURLSession ... ..."
Я как раз в ситуации небольшой сетевой задачи: я хочу общаться с остальным API на нашем сервере, публиковать те же данные и получать ответ данных. Я создаю один сеанс по умолчанию и выполняю одну или две задачи, синхронизируя их с помощью группы диспетчеризации, как в этом примере:
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(@"AppDelegate application:performFetchWithCompletionHandler:");
BOOL thereIsALoggedInUser = [self thereIsALoggedInUser];
if (!(thereIsALoggedInUser && [application isProtectedDataAvailable])){
completionHandler(UIBackgroundFetchResultNoData);
NSLog(@"completionHandler(UIBackgroundFetchResultNoData): thereIsALoggedInUser: %@, isProtectedDataAvailable: %@",
thereIsALoggedInUser ? @"YES" : @"NO",
[application isProtectedDataAvailable] ? @"YES" : @"NO");
return;
}
NSArray<NSString*> *objectsIWantToSend = [self getObjectsIWantToSend];
if (!objectsIWantToSend) {
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever];
completionHandler(UIBackgroundFetchResultNoData);
NSLog(@"No post data");
} else {
[self sendToServer:objectsIWantToSend usingCompletionHandler:completionHandler];
}
}
-(void) sendToServer:(NSArray<NSString*> *)objectsArray usingCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(@"AppDelegate - sendToServer:usingCompletionHandler:");
__block BOOL thereWasAnError = NO;
__block BOOL thereWasAnUnsuccessfullHttpStatusCode = NO;
dispatch_group_t sendTransactionsGroup = dispatch_group_create();
NSURLSession *postSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
for (NSString *objectToPost in objectsArray) {
dispatch_group_enter(sendTransactionsGroup);
NSMutableURLRequest *request = [NSMutableURLRequest new];
[request setURL:[NSURL URLWithString:@"restApiUrl"]];
[request setHTTPMethod:@"POST"];
request = [Util setStandardContenttypeAuthorizationAndAcceptlanguageOnRequest:request];
[request setHTTPBody:[NSJSONSerialization dataWithJSONObject:@{ @"appData": objectToPost } options:kNilOptions error:nil]];
NSURLSessionDataTask *dataTask = [postSession dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
NSInteger statusCode = -1;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
statusCode = [(NSHTTPURLResponse *)response statusCode];
}
if (error) {
NSLog(@"Error");
thereWasAnError = YES;
} else if (statusCode != 201) {
NSLog(@"Error: http status code: %d", httpResponse.statusCode);
thereWasAnUnsuccessfullHttpStatusCode = YES;
} else {
[self parseAndStoreData:data];
}
dispatch_group_leave(sendTransactionsGroup);
}];
[dataTask resume];
}
dispatch_group_notify(sendTransactionsGroup, dispatch_get_main_queue(),^{
if (thereWasAnError) {
completionHandler(UIBackgroundFetchResultFailed);
} else if (thereWasAnUnsuccessfullHttpStatusCode) {
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalNever];
completionHandler(UIBackgroundFetchResultNoData);
} else {
completionHandler(UIBackgroundFetchResultNewData);
}
});
}
В моем приложении: метод didFinishLaunchingWithOptions: я устанавливаю для BackgroundFetchInterval значение MinimumInterval, используя
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
Я не закрываю приложение на устройстве принудительно, потому что знаю, что в этом случае система не будет вызывать метод AppDelegate application:performFetchWithCompletionHandler: до тех пор, пока пользователь не перезапустит приложение.
Я использую несколько тестовых случаев с несколькими комбинациями устройств, заблокированных/разблокированных, заряжающихся/не заряжающихся, все в сети Wi-Fi, потому что у них нет симов.
Когда я запускаю вышеупомянутый тип кода, используя Xcode -> Debug -> Simulate background fetch или используя схему сборки с выбранной опцией «запуск из-за события фоновой выборки», все идет хорошо: сервер получает мой почтовый запрос, приложение правильно получает ответ сервера, и выполняется блок dispatch_group_notify. Тестируя код через реальное устройство, я не получаю никакого результата, даже вызова на сервер.
applicationWillResignActive
, но никогда неperformFetchWithCompletionHandler
? (Если это так, тоsendToServer
не имеет значения.) Тогда я бы посоветовал две вещи: 1. Полностью удалить и переустановить приложение (если вы еще этого не сделали), чтобы сбросить фоновую загрузку. 2. Создайте новый MCVE, который выполняет фоновую выборку (с некоторойNSURLSession
задачей) и убедитесь, что в вашем приложении нет других помех. с процессом. - person Rob   schedule 24.05.2019