如何在后台从配套手表应用程序启动主 iOS 应用程序?

How to start main iOS app from companion watch app in background?

情况:

由于我们的用户已将他们的 iOS 更新为 11 and/or WatchOS 为 4,我们的 iOS 应用程序似乎不会在我们启动应用程序时触发任何预定的计时器WatchOS 应用程序。从 WatchOS 应用程序启动我们的主应用程序时,我们可能做错了什么。

上下文和代码:

我们的 WatchOS 应用程序是一个配套应用程序,让用户 start/stop 我们的 iPhone 应用程序在后台通过按一个按钮。我们使用:

func startMainApp() {
    guard WCSession.default().isReachable == true else {
        print("Watch is not reachable")
        return
    }

    var data = [String : AnyObject]()
    data[WatchActions.actionKey()] = NSNumber.init(value: WatchActions.startApp.rawValue as Int)

    WCSession.default().sendMessage(data, replyHandler: { (result: [String : Any]) in
        let resultNumber = result[WatchActions.resultKey()] as? NSNumber
        let resultBool = resultNumber!.boolValue
        if resultBool == true {
            self.setModeActivated()
        } else {
            self.setModeActivationFailed()
        }

    }) { (error: Error) in
        if (error as NSError).code != 7012 {
            print("start app error: \(error.localizedDescription)")
            self.setModeActivationFailed()
        }
    }
}

然后在我们的主应用程序中,我们收到消息并启动我们的基本控制器:

func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
    if let actionNumber : NSNumber = message[WatchActions.actionKey()] as? NSNumber {
        if let watchAction : WatchActions = WatchActions(rawValue: actionNumber.intValue) {

            switch(watchAction) {
                case .isAppActive:
                    let result = BaseController.sharedInstance.sleepAndWakeUpController.isAwake()
                     replyHandler([WatchActions.resultKey() : NSNumber.init(value: result as Bool)])
                return

                case .startApp:
                    AudioController.sharedInstance().playActivatedSound()

                    let isRunningOnForeground = ApplicationStateHelper.isActive()
                    if isRunningOnForeground == false {
                        BaseController.sharedInstance.start(inBackground: true)
                    }
                    let result = true
                    replyHandler([WatchActions.resultKey() : NSNumber.init(value: result as Bool)])

                    DDLogInfo("[APPLE WATCH] [didReceiveMessage] [.startApp]")
                return
            }
        }
    }

    replyHandler([WatchActions.resultKey() : NSNumber.init(value: false as Bool)])
    return
}

一切似乎都像以前一样工作,我们正确地获得了 GPS 位置,我们所有的进程都开始了,但是,Timer 开始的对象没有触发。

这在 iOS 10 之前工作得很好,所以我怀疑这与 iOS 11 工作不同的背景状态有关。但是,我似乎找不到这方面的任何文档。

额外信息:

问题: 从手表应用程序启动我们的主应用程序的最佳方式是什么? iOS 11/WatchOS 4 关于背景状态有什么变化吗?我可以找到这方面的文档吗?这可能是一个 iOS 错误吗?

当应用程序关闭且不在后台 运行 时,位置永远不会在 iOS 11 中跟踪,这不是 iWatchOS 4 和 iOS 11 错误。

iOS11

中位置跟踪的变化

follow this documentation link: Changes to location tracking in iOS 11 iOS 11 also makes some major changes to existing APIs. One of the affected areas is location tracking.

如果您的应用仅在应用处于前台时使用位置信息(就像大多数应用一样),您可能根本不需要更改任何内容;但是,如果它是那些全天持续跟踪用户位置的应用程序之一,您可能应该在今年夏天预订一些时间来对跟踪和测试可能的使用场景的方式进行一些更改。

For example, let’s consider those two apps again; the continuous background location and the significant location change monitoring app. Suppose the user goes for a run with the continuous background location app. They’ll go on the run, they’ll come back, they’ll see the solid arrow the whole time, and when they look at their map, they’ll see every twist and turn they took. When they install that app that uses significant location change monitoring, they’ll see the same thing, a solid arrow. As far as the user is aware, this app is probably receiving the same amount of information as their run tracking app. So if users are misinterpreting our signals, we decided the best way to fix this was to adjust how we indicate location usage.

watchOS 可以使用 iOS 应用中的许多相同技术;然而,即使一项技术可用,您也可能无法像在 iPhone.

上那样使用它

Do not use background execution modes for a technology. In general, Watch apps are considered foreground apps; they run only while the user interacts with one of their interfaces. As a result, the corresponding WatchKit extension cannot take advantage of most background execution modes to perform tasks. might be help this documentation: Leveraging iOS Technologies for watch

我所能提供的只是确认此行为确实从 iOS 10 更改为 iOS 11。我怀疑 iOS 10 上的行为 (和更早?)是不正确的。 Apple 对改变 inadvertent/what 他们认为不正确的行为没有任何疑虑,即使开发人员开始依赖该行为(我很确定我在上一个手表项目中使用了这种行为)。

事实是 UIApplication 通过手表消息启动时的状态是 background。当应用程序在后台时,计时器不应该 运行,除非使用特定的后台执行 modes/background 任务。这个事实是众所周知的,并且通常在 iOS 开发人员职业生涯的早期就遇到过。事实上,当从手表启动时,计时器会 运行 在后台,我可以推测,这是一个错误。

我不知道你的用例,即你为什么依赖那些计时器,但你可以做的一件事很简单,就是创建一个空的后台任务,这会让你在应用程序运行时有更多的时间推出。

var backgroundTask: UIBackgroundTaskIdentifier?
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "app Start Task", expirationHandler: {
    guard let task = backgroundTask else { return }
    UIApplication.shared.endBackgroundTask(task)
})

let timer = Timer(timeInterval: 1, repeats: true) { (timer) in
    print("Running")
}

如果您需要一个更一致、更长的 运行ning 解决方案,您可能需要利用您的位置更新作为一个机会来完成计时器当前的任何工作。还有很多其他背景模式可以追求。

问题总结:

问:从手表应用程序启动我们的主应用程序的最佳方式是什么?
答:您建议的代码是启动配套应用程序的好方法。

问:iOS 11/WatchOS 4 关于背景状态有什么变化吗?
答:不,尤其是在计时器方面。不同的行为可能是一种修正。

问:我能找到这方面的文档吗?
答:我不能。有时,您可以在论坛上或通过您的开发者帐户通过代码级支持问题或访问 WWDC 从 Apple 工程师那里获取这些信息。

问:这可能是 iOS 错误吗?
A: 较早的行为可能是错误。