位置服务在 WatchKit 调用的后台不工作

Location Services not working in background called by WatchKit

我的 Apple Watch 应用程序需要一些数据并从相应的 iPhone 应用程序请求它。为满足请求,iPhone 应用需要用户位置信息。 在收到并使用真正的 Apple Watch 进行测试后,我发现当 运行 在后台时,我的 iPhone 应用程序没有收到位置更新。如果 iPhone 应用程序在前台处于活动状态,则它可以正常运行。使用模拟器,它在两种情况下都有效。

在这两种情况下(活动和后台),WatchKit 扩展调用并成功启动 iPhone 应用程序,并一直进行到 iPhone 应用程序中调用 startUpdatingLocation。但是,如果应用程序 运行 在后台,则永远不会调用 didUpdateLocations。

我尝试使用 requestAlwaysAuthorization 和 requestWhenInUseAuthorization。没有不同。 然后我还激活了功能中的 "location updates" 后台模式。但同样没有区别。

有没有其他人遇到同样的问题并找到了在后台接收位置的方法?

这里是一些代码。首先检查是否需要授权。

// 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];
}

需要检查和注意的三件事:

  1. [self.locationManager requestAlwaysAuthorization]; 这样的位置权限只被 OS 确认一次。如果您已经请求权限,无论级别如何,OS 都不会向用户显示请求。 OS 将只传递请求并保持权限级别不变。只有在 [CLLocationManager authorizationStatus] returns kCLAuthorizationStatusNotDetermined 时,您才能确信 OS 会向用户显示请求。在所有其他情况下,您必须通过显示警报或其他形式的 UI 显示来手动请求权限。另请注意,OS 会保留它是否已显示请求,即使您删除应用程序并重新安装也是如此。因此,要进行测试,您需要重置模拟器的内容或 iPhone 的位置隐私。

  2. 确保您已经为 NSLocationAlwaysUsageDescriptionNSLocationWhenInUseUsageDescription 添加了 plist 键 如果您不将其添加到您的 plist,OS 将忽略任何位置权限请求。

  3. 如果您想在 phone 应用程序处于后台时使用 requestAlwaysAuthorization 从 phone(不是手表应用程序扩展)获取位置数据,还需要您在项目>目标>功能下注册后台模式位置更新。

更新 使用后台任务让您的应用有时间在后台响应。像这样:

-(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;
    });
}