WatchOS2 WCSession sendMessage 不会在后台唤醒 iPhone

WatchOS2 WCSession sendMessage doesn't wake iPhone on background

这正在模拟器和真实物理设备上进行测试 iphone5s。我尝试使用 WCSession sendMessage 从 WatchOS2 扩展到 iPhone iOS9 代码进行通信。当 iphone 应用程序处于 运行 时,无论是在前台还是后台模式下,它都能很好地工作。

但是如果我终止 iPhone 应用程序(根本不是 运行 应用程序),那么我总是会遇到 errorHandler 超时。因此 Watch 无法再与 iPhone 通信。

"Error Domain=WCErrorDomain Code=7012 "Message reply took too long." UserInfo={NSLocalizedDescription=Message reply took too long., NSLocalizedFailureReason=Reply timeout occured.}".

我认为它应该在后台唤醒 iPhone 应用程序。

知道如何解决或修复此问题吗?谢谢!

好吧,您可以使用 transferUserInfo 来对呼叫进行排队。使用 sendMessage 会在应用程序被杀死时导致错误

激活 AppDelegate didFinishLaunchingWithOptions 方法中的 WCSession 很重要。您还必须在那里设置 WCSessionDelegate 。如果你在其他地方做,当系统在后台启动被杀死的应用程序时,代码可能不会被执行。

此外,您应该通过 replyHandler 发送回复。如果您尝试以其他方式发送,系统会等待一个永远不会到来的回复。因此超时错误。

下面是一个应用被杀死时唤醒应用的例子:

在 WatchExtension 中:

设置会话。通常在您的 ExtensionDelegate 中:

func applicationDidFinishLaunching() {
    if WCSession.isSupported() {
        let session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()
    }
}

然后在您需要应用程序中的内容时发送消息:

if WCSession.defaultSession().reachable {
    let messageDict = ["message": "hello iPhone!"]
    WCSession.defaultSession().sendMessage(messageDict, replyHandler: { (replyDict) -> Void in
        print(replyDict)
        }, errorHandler: { (error) -> Void in
        print(error)
    }
}

在 iPhone 应用中:

相同的会话设置,但这次还设置了委托:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    ...
    if WCSession.isSupported() {
        let session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()
    }
}

然后实现委托方法将回复发送给手表:

func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
    replyHandler(["message": "Hello Watch!"])
}

只要 Watch 和 iPhone 之间存在连接,它就可以工作。如果应用程序不是 运行,系统会在后台启动它。

我不知道系统是否等待足够长的时间直到您从 iCloud 收到数据,但这个例子肯定会唤醒应用程序。

经过数小时的尝试和@jeron 的提示。我终于自己弄清楚了问题。

在我的 session:didReceiveMessage 委托方法中,我有两个调用。 1.replyHandler打电话。 2. 在我的案例中,我有一个异步进程 运行 (RXPromise),它嵌套了很多 RXPromise 回调以从云服务中获取各种数据。我没有注意它,因为它应该立即调用 return。但是现在我把 RXPromise 块全部注释掉了,它每次都可以在后台唤醒 iOS 应用程序。

最后我弄清楚问题是因为在 RXPromise 调用之后,不再保证返回主线程。而且我相信 session:didReceiveMessage 必须是 return 在主线程上。我没有在 Apple 文档的任何地方看到这一点。

最终解决方案:

- (void)session:(WCSession *)session
    didReceiveMessage:(NSDictionary<NSString *, id> *)message
         replyHandler:(void (^)(NSDictionary<NSString *, id> *_Nonnull))replyHandler {

    replyHandler(@{ @"schedule" : @"OK" });

    dispatch_async(dispatch_get_main_queue(), ^{
      Nested RXPromise calls.....
    });

}