Подтвердить что ты не робот

Устранение неполадок при загрузке фонового приложения iOS не работает?

Я пытаюсь получить фоновое приложение iOS для работы в моем приложении. При тестировании в Xcode он работает, при работе на устройстве это не так!

  • В моем тестовом устройстве работает iOS 9.3.5 (моя цель развертывания - 7.1).
  • Я включил "Фоновая выборка" в разделе "Фоновые режимы" в разделе "Возможности" для цели в Xcode

введите описание изображения здесь

В приложении: didFinishLaunchingWithOptions Я пробовал различные интервалы с помощью setMinimumBackgroundFetchInterval, включая UIApplicationBackgroundFetchIntervalMinimum

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    // tell the system we want background fetch
    //[application setMinimumBackgroundFetchInterval:3600]; // 60 minutes
    [application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
    //[application setMinimumBackgroundFetchInterval:1800]; // 30 minutes

    return YES;
}

Я реализовал приложение: performFetchWithCompletionHandler

void (^fetchCompletionHandler)(UIBackgroundFetchResult);
NSDate *fetchStart;

-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    fetchCompletionHandler = completionHandler;

    fetchStart = [NSDate date];

    [[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];
    [[NSUserDefaults standardUserDefaults] synchronize];

    [FeedParser parseFeedAtUrl:url withDelegate:self];
}

 -(void)onParserFinished
{
    DDLogVerbose(@"AppDelegate/onParserFinished");

    UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;

    NSDate *fetchEnd = [NSDate date];
    NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
    DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);
    if ([self.mostRecentContentDate compare:item.date] < 0) {
        DDLogVerbose(@"got new content: %@", item.date);
        self.mostRecentContentDate = item.date;
        [self scheduleNotificationWithItem:item];
        result = UIBackgroundFetchResultNewData;
    }
    else {
        DDLogVerbose(@"no new content.");
        UILocalNotification* localNotification = [[UILocalNotification alloc] init];
        localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:60];
        localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];
        localNotification.timeZone = [NSTimeZone defaultTimeZone];
        [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
    }

    fetchCompletionHandler(result);
}
  • У меня (успешно!) протестировано с помощью симулятора и устройства с использованием Xcode Debug/SimulateBackgroundFetch

  • Я успешно протестировал новую схему, как показано в другом ответе SO (qaru.site/info/202875/...)

  • Мои тесты показывают, что код выполняется в методе performFetch примерно через 0,3 секунды (поэтому он не занимает много времени).
  • Я проверил, что устройство имеет обновление фона в настройках.
  • Конечно, я рассмотрел другие вопросы SO, надеясь, что кто-то другой испытал одно и то же.:)

При работе на устройстве и не подключенном к Xcode мой код не выполняется. Я открыл приложение, закрыл приложение (не убил приложение!), Ждал часов и дней. Я попробовал регистрацию в обработчиках извлечения, а также написал код для отправки локальных уведомлений.

Я как-то успешно увидел мой локальный тест на устройствах, и на самом деле iOS, похоже, запускал выборку три раза, каждый около пятнадцати минут друг от друга, но потом это никогда не повторялось.

Я знаю, что алгоритм, используемый для определения того, как часто разрешать фоновый выбор, является загадкой, но я ожидаю, что он будет работать хотя бы изредка в течение нескольких дней.

Я не понимаю, что еще нужно проверить, или как устранить неполадку, почему он работает в симуляторе, но не на устройстве.

Оцените любые советы!

4b9b3361

Ответ 1

Ваша проблема в том, что вы возвращаетесь с performFetchWithCompletionHandler до вызова обработчика завершения, поскольку операция сетевой выборки происходит в фоновом режиме, и вы вызываете обработчик завершения в свой метод делегирования. Поскольку iOS думает, что вы не играете по правилам, это отрицает вашу способность использовать фоновый выбор.

Чтобы устранить проблему, вам необходимо вызвать beginBackgroundTaskWithExpirationHandler, а затем завершить эту задачу после вызова обработчика завершения.

Что-то вроде:

UIBackgroundTaskIdentifier backgroundTask

-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    fetchCompletionHandler = completionHandler;

    fetchStart = [NSDate date];

    self.backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
        [application endBackgroundTask:self.backgroundUpdateTask];
        self.backgroundTask = UIBackgroundTaskInvalid;
    }];

    [[NSUserDefaults standardUserDefaults] setObject:fetchStart forKey:kLastCheckedContentDate];

    [FeedParser parseFeedAtUrl:url withDelegate:self];
}

-(void)onParserFinished
{
    DDLogVerbose(@"AppDelegate/onParserFinished");

    UIBackgroundFetchResult result = UIBackgroundFetchResultNoData;

    NSDate *fetchEnd = [NSDate date];
    NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
    DDLogVerbose(@"Background Fetch Duration: %f seconds", timeElapsed);
    if ([self.mostRecentContentDate compare:item.date] < 0) {
        DDLogVerbose(@"got new content: %@", item.date);
        self.mostRecentContentDate = item.date;
        [self scheduleNotificationWithItem:item];
        result = UIBackgroundFetchResultNewData;
    }
    else {
        DDLogVerbose(@"no new content.");
        UILocalNotification* localNotification = [[UILocalNotification alloc] init];
        localNotification.alertBody = [NSString stringWithFormat:@"Checked for new posts in %f seconds", timeElapsed];
        [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
    }
    fetchCompletionHandler(result);
    [[UIApplication sharedApplication] application endBackgroundTask:self.backgroundUpdateTask];
    self.backgroundTask = UIBackgroundTaskInvalid;
}

Мое тестовое приложение, использующее этот подход, сначала выполняет выборку каждые 15 минут, но со временем становится менее частым. Без фоновой задачи он обнаружил ту же проблему, что и вы.

Я обнаружил, что настройка интервала выборки фона для чего-то другого, кроме UIApplicationBackgroundFetchIntervalMinimum, также помогает. Мое тестовое приложение работает с интервалом выборки фона 3600 (один час) и надежно запускается в течение нескольких дней; даже после перезагрузки телефона и повторного запуска приложения. Фактический интервал срабатывания составляет 2-3 часа.

Мое примерное приложение здесь

Ответ 2

Чтобы реализовать фоновый выбор, вам нужно сделать три вещи:

  • Установите флажок "Фоновая выборка в фоновых режимах ваших приложений".
  • Используйте setMinimumBackgroundFetchInterval (_:), чтобы установить временной интервал, подходящий для вашего приложения.
  • Внедрите приложение (_: performFetchWithCompletionHandler:) в своем делете приложения для обработки фоновой выборки.

Частота выборки фона

Как часто наше приложение может выполнять фоновое извлечение, определяется системой и зависит от:

  • Доступно ли подключение к сети в это конкретное время.
  • Не работает ли устройство, то есть работает
  • Сколько времени и данных потребляет ваше приложение в предыдущем Фоновая выборка

Другими словами, ваше приложение полностью находится во власти системы, чтобы запланировать фоновый выбор для вас. Кроме того, каждый раз, когда ваше приложение использует фоновый выбор, для завершения процесса требуется не более 30 секунд.

Включить в свой AppDelegate (измените приведенный ниже код на ваши потребности)

-(void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    NSLog(@"Background fetch started...");

    //---do background fetch here---
    // You have up to 30 seconds to perform the fetch

    BOOL downloadSuccessful = YES;

    if (downloadSuccessful) {
        //---set the flag that data is successfully downloaded---
        completionHandler(UIBackgroundFetchResultNewData);
    } else {
        //---set the flag that download is not successful---
        completionHandler(UIBackgroundFetchResultFailed);
    }

    NSLog(@"Background fetch completed...");
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
    return YES;
}

Чтобы проверить, включено ли Background Refresh для вашего приложения, используйте приведенный ниже код:

UIBackgroundRefreshStatus status = [[UIApplication sharedApplication] backgroundRefreshStatus];
switch (status) {
    case UIBackgroundRefreshStatusAvailable:
    // We can do background fetch! Let do this!
    break;
    case UIBackgroundRefreshStatusDenied:
    // The user has background fetch turned off. Too bad.
    break;
    case UIBackgroundRefreshStatusRestricted:
    // Parental Controls, Enterprise Restrictions, Old Phones, Oh my!
    break;
}