ios - 如何在后台从配套 watch 应用程序启动主 iOS 应用程序?

标签 ios iphone swift watchos wcsession

情况:

由于我们的用户已将他们的 iOS 更新到 11 和/或 WatchOS 到 4,因此当我们的 WatchOS 应用启动应用时,我们的 iOS 应用似乎没有触发任何预定的计时器。从 WatchOS 应用程序启动我们的主应用程序时,我们可能做错了什么。

上下文和代码:

我们的 WatchOS 应用程序是一款配套应用程序,用户可以通过按下按钮在后台启动/停止我们的 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()
        }
    }
}

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

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 10 上,当我们以这种方式启动我们的主应用程序时,该应用程序在 iPhone 的多任务 View 中可见。现在在 iOS 11 上,它在多任务 View 中不可见,但它确实在后台运行。我成功地看到了我在后台安排的本地通知,我可以通过事件代码进行调试,当点击应用程序图标时,该应用程序立即可用。
  • 我们的 WatchOS 应用的部署目标是 2.0
  • 通过 XCode 调试并连接设备,使用调试-->附加到 PID 或名称-->输入的应用程序名称。然后从 Apple Watch 启动我们的应用程序并进行调试。
  • 可在 iPhone 6 上使用 WatchOS 4.0 在 iOS 11.0.3 上重现

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

最佳答案

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

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

我不知道你的用例,即你为什么依赖那些计时器,但你可以做的一件事非常简单,就是创建一个空的后台任务,它会在应用程序启动时让你有更多的时间.

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")
}

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

问题总结:

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

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

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

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

关于ios - 如何在后台从配套 watch 应用程序启动主 iOS 应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47507582/

相关文章:

ios - NSDictionary 初始化(contentsOfFile :) is deprecated so now what?

ios - Sprite Kit 碰撞错误 - Swift

ios - 如何为具有命名参数的完成处理程序创建类型别名

ios - Int 不可转换为 DictionaryIndex<String, String>

ios - Swift 中触发 UITextField 错误

ios - 在 UICollectionView 中为单元格设置边界和调整大小

ios - 如何从 VNClassificationObservation 获取对象矩形/坐标

objective-c - Firebase FCM 实例 ID 为零(在真实设备上运行快速启动应用程序)

ios - 通过 iTunes 在 iPhone 文档文件夹中查找下载的文件

iphone - 手机上的高度测量有多准确