Службы геолокации не работают в фоновом режиме, вызванном WatchKit

Приложение My Apple Watch требует некоторых данных и запрашивает их из соответствующего приложения iPhone. Для выполнения запроса приложению iPhone требуется местоположение пользователя. После получения и тестирования с настоящими Apple Watch я обнаружил, что мое приложение для iPhone не получает обновления местоположения при работе в фоновом режиме. Если приложение iPhone активно на переднем плане, оно работает без проблем. С симулятором в обоих случаях все получилось.

В обоих случаях (активном и фоновом) расширение WatchKit вызывает и успешно запускает приложение iPhone и работает до тех пор, пока в приложении iPhone не будет вызван startUpdatingLocation. Но если приложение работает в фоновом режиме, didUpdateLocations никогда не вызывается.

Я пробовал использовать requestAlwaysAuthorization, а также requestWhenInUseAuthorization. Нет разницы. Я также активировал фоновый режим "обновления местоположения" в пределах возможностей. Но опять же без разницы.

Кто-нибудь еще сталкивался с такой же проблемой и нашел способ получить местоположение также в фоновом режиме?

Вот код. Сначала проверьте, требуется ли авторизация.

// iOS 8 check to avoid crash on older iOS
    if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])
    {
        [self requestLocationAlwaysAuthorization];
    }
    else
    {
        [self runLocationUpdate];
    }

Здесь проверка правильности прав диспетчера местоположения.

- (void)requestLocationAlwaysAuthorization
{
CLAuthorizationStatus currentAuthStatus = [CLLocationManager authorizationStatus];

if (currentAuthStatus == kCLAuthorizationStatusDenied)
{
    //request user to change setting
}
else if (currentAuthStatus == kCLAuthorizationStatusRestricted)
{
    //request user to change setting
}
else if (currentAuthStatus == kCLAuthorizationStatusNotDetermined)
{
    [self.locationManager requestAlwaysAuthorization];

    [self runLocationUpdate];
}
else if (currentAuthStatus == kCLAuthorizationStatusAuthorizedWhenInUse)
{
    //maybe when in use is also enough?
    [self runLocationUpdate];
}
else if (currentAuthStatus == kCLAuthorizationStatusAuthorizedAlways)
{
    //all ok
    [self runLocationUpdate];
}

}

Здесь вызов startUpdatingLocation. Делегат didUpdateLocations будет вызываться только тогда, когда приложение iPhone активно.

-(void)runLocationUpdate
{
    [self.locationManager startUpdatingLocation];
}

person Welu    schedule 14.05.2015    source источник
comment
Вы можете показать код? В зависимости от того, когда и как вы запросили requestAlwaysAuthorization, может возникнуть проблема.   -  person rmp    schedule 15.05.2015
comment
Спасибо за идею, код добавил. Проблема также возникает, когда requestAlwaysAuthorization не вызывается, потому что настройка уже всегда соответствует требованиям. Вызов requestAlwaysAuthorization приводит к правильному изменению настроек конфиденциальности iPhone.   -  person Welu    schedule 15.05.2015
comment
Кстати, похоже, что kCLAuthorizationStatusAuthorizedAlways больше не нужен, когда приложение iPhone работает в фоновом режиме. Статус kCLAuthorizationStatusAuthorizedWhenInUse теперь достаточно.   -  person Welu    schedule 16.05.2015
comment
Будьте осторожны, если вы не вызываете диспетчер местоположения из расширения приложения для часов, по моему опыту, во время использования разрешение не работает, если приложение не находится на переднем плане. Это работает на симуляторе, но не на настоящих часах.   -  person rmp    schedule 16.05.2015
comment
Здесь последнее обновление после того, как принадлежащее приложение успешно работает с реальными устройствами и доступно в магазине приложений: RequestWhenInUseAuthorization достаточно, приложение iPhone обрабатывается как на переднем плане при вызове из Watch, даже в фоновом режиме. Также нет необходимости в фоновом режиме. Единственное, что требуется, - это использование фоновой задачи, как объясняется rmp здесь, в решении. Еще раз спасибо rmp!   -  person Welu    schedule 09.06.2015
comment
Поздравляю с поступлением вашего приложения в магазин!   -  person rmp    schedule 10.06.2015


Ответы (1)


Три вещи, которые нужно проверить и о которых нужно знать:

  1. Разрешения на местоположение, такие как [self.locationManager requestAlwaysAuthorization];, подтверждаются ОС только один раз. Если вы уже запрашивали разрешение, независимо от уровня, ОС НЕ будет отображать запрос пользователю. ОС просто передаст запрос и оставит уровень разрешений как есть. Единственный раз, когда вы можете быть уверены, что ОС отобразит запрос пользователю, - это если [CLLocationManager authorizationStatus] вернет kCLAuthorizationStatusNotDetermined. В любом другом случае вы должны вручную запросить разрешение, отобразив предупреждение или другую форму отображения пользовательского интерфейса. Также обратите внимание, что ОС сохраняет информацию о том, отображал ли она уже запрос, даже если вы удалите приложение и переустановите его. Итак, чтобы проверить, вам нужно сбросить содержимое вашего симулятора или конфиденциальность местоположения вашего iPhone.

  2. Убедитесь, что вы добавили ключи plist для NSLocationAlwaysUsageDescription И NSLocationWhenInUseUsageDescription. Если вы не добавите их в свой список, ОС проигнорирует любые запросы разрешения местоположения.

  3. Если вы хотите использовать requestAlwaysAuthorization для получения данных о местоположении с телефона (а не расширения приложения для часов), когда приложение телефона находится в фоновом режиме, вам также потребуется зарегистрироваться для получения обновлений местоположения в фоновых режимах в разделе «Проект»> «Цель»> «Возможности».

ОБНОВЛЕНИЕ Используйте фоновую задачу, чтобы дать вашему приложению время для ответа в фоновом режиме. Что-то вроде этого:

-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply{

    UIApplication *app = [UIApplication sharedApplication];

    UIBackgroundTaskIdentifier bgTask __block = [app beginBackgroundTaskWithName:@"watchAppRequest" expirationHandler:^{

        [[UIApplication sharedApplication] endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;

    }];

//make your calls here to your tasks, when finished, send the reply then terminate the background task

//send reply back to watch
reply(replyInfo);

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        [app endBackgroundTask:bgTask];
        bgTask=UIBackgroundTaskInvalid;
    });
}
person rmp    schedule 15.05.2015
comment
Мой опыт в отношении упомянутых трех тем: 1.) Я использую UIAlertController, чтобы информировать пользователя и открывать диалоговое окно настроек, если пользователю нравится, с помощью UIApplicationOpenSettingsURLString 2.) Точно, у меня есть обе строки, и я нахожу эти две настройки при использовании и всегда также в приложении настроек iPhone. 3.) Правильно, как уже упоминалось, я тоже это сделал. Первые два были также необходимы, чтобы запустить его в симуляторе. Но они все еще не работают на реальном устройстве Watch, когда приложение iPhone находится в фоновом режиме. - person Welu; 15.05.2015
comment
Вы случайно не используете другие службы или технологии, которым нужен фоновый доступ? Вы используете связку ключей для аутентификации? - person rmp; 15.05.2015
comment
Кроме того, заключена ли эта функция в BackgroundTask? - person rmp; 15.05.2015
comment
После того, как я получаю данные о местоположении пользователей, приложение вызывает сервер для получения необходимых данных. Поэтому я также установил режим фоновой выборки для фоновых возможностей. Но поскольку didUpdateLocations не вызывается, когда приложение iPhone находится в фоновом режиме, оно не вызывает сервер. Нет проблем, когда приложение для iPhone активно. Связка ключей в приложении не используется. - person Welu; 15.05.2015
comment
А как насчет использования BackgroundTask? Обернуты ли ваши вызовы в BackgroundTask, чтобы дать этому приложению время для ответа в фоновом режиме? - person rmp; 15.05.2015
comment
Вы говорите, что startUpdatingLocation и / или didUpdateLocations должны быть каким-то образом обернуты в фоновую задачу? - person Welu; 15.05.2015
comment
Большое спасибо, фоновые задачи, похоже, подходят. Приложение получило местоположение теперь также, когда приложение iPhone находится в фоновом режиме. - person Welu; 15.05.2015