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

Posted

技术标签:

【中文标题】如何在后台从伴侣手表应用程序启动主 iOS 应用程序?【英文标题】:How to start main iOS app from companion watch app in background? 【发布时间】:2017-11-27 09:28:16 【问题描述】:

情况:

由于我们的用户已将他们的 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()
        
    

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

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

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

【问题讨论】:

您提到计时器不工作,但您分享的代码没有计时器不触发的示例。 【参考方案1】:

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

事实上,UIApplication 的状态在由手表消息启动时是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")

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

您的问题总结:

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

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

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

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

【讨论】:

【参考方案2】:

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

iOS 11 中位置跟踪的变化

点击此文档链接:Changes to location tracking in iOS 11 iOS 11 还对现有 API 进行了一些重大更改。受影响的领域之一是位置跟踪。

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

例如,让我们再次考虑这两个应用程序;连续的后台位置和重大位置变化监控应用程序。假设用户使用连续后台定位应用程序进行跑步。他们会继续奔跑,他们会回来,他们会一直看到实心箭头,当他们查看地图时,他们会看到他们所经历的每一个曲折。当他们安装使用显着位置变化监控的应用程序时,他们会看到同样的东西,一个实心箭头。据用户所知,这个应用程序接收的信息量可能与他们的跑步跟踪应用程序相同。因此,如果用户误解了我们的信号,我们决定解决此问题的最佳方法是调整我们指示位置使用情况的方式。

watchOS 可以使用 iOS 应用中的许多相同技术;但是,即使一项技术可用,您也可能无法像在 iPhone 上那样使用它。

不要对技术使用后台执行模式。通常,Watch 应用被视为前台应用;它们仅在用户与其界面之一交互时运行。因此,相应的 WatchKit 扩展无法利用大多数后台执行模式来执行任务。可能有助于此文档: Leveraging iOS Technologies for watch

【讨论】:

我了解 iOS 11 中的权限更改。我们会根据用户偏好“始终”或“使用中”跟踪用户位置。但是,我认为这与我们这里的问题无关。我们的 WatchOS 应用程序是一个仅限前台的应用程序,当您已经授予位置权限时,它只是在后台“打开”我们的应用程序的开关。自 iOS 11 以来,我看到的唯一变化是 Timers 不起作用。位置更新似乎正常进行。

以上是关于如何在后台从伴侣手表应用程序启动主 iOS 应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何从后台任务在主应用程序上执行功能

如何从 ios 应用程序获取手表套件扩展集中的通知?

如何禁用虚拟伴侣应用程序(只应提取磨损部分进行佩戴)

如何在 iOS 中创建服务?

iOS WatchKit - 启动手表应用程序“SPErrorInvalidBundleNoGizmoBinaryMessage”时出错

如何在模拟器中看到苹果手表的主屏幕?