Apple Watch 扩展和主机应用程序之间的双向通信

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

这一点非常重要,因为 Apple Watch 为 iOS 设备上运行的主应用程序提供了第二个屏幕/外围免费体验——无论是远程控制,还是快速查看/浏览大局中发生的事情。

上一篇文章 中,我展示了如何使用 IBM MobileFirst Platform Foundation 服务器在 Apple Watch 应用程序中设置远程日志记录和检测/分析。我使用下面描述的方法在上一篇文章的示例应用程序中的 WatchKit 和主机应用程序之间进行通信。

当我们谈论双向通信时,我们谈论的是通过两种方式发送数据:

  1. 将数据从主机应用程序发送到 WatchKit 应用程序
  2. 从主机应用向 WatchKit 应用发送数据

乍一看,您可能会想“哦,这很简单,只需使用 NSNotificationCenter 在应用程序的各个类之间进行通信”,但事情并没有那么简单。

Apple Watch 应用程序实际上由 3 个部分组成:1) 主要的 iOS 应用程序二进制文件,2) Apple Watch 上的用户界面,以及 3) WatchKit 扩展二进制文件(在 iOS 设备上)。

Apple Watch App – 架构组件

是的,你没看错,WatchKit 扩展(控制 Apple Watch UI 内部的所有逻辑并驻留在 iOS 设备上)是一个独立于“主”iOS 应用程序二进制文件的二进制文件。这些是独立的进程,因此主应用内存中的对象与扩展内存中的对象不同,因此这些进程不直接通信。 NSNotificationCenter 不会工作。

但是,您肯定有办法让这种场景发挥作用。

首先,WatchKit 具有从 WatchKit 扩展调用主机应用程序上的操作的方法。 WatchKit 的 openParentApplication handleWatchKitExtensionRequest 方法都提供了在包含应用程序中调用操作和传递数据的能力,并提供了一种机制来在主机应用程序中的代码完成后调用 WatchKit 扩展中的“回复”代码块。

例如, 在 WatchKit 扩展中 ,这将调用主机应用程序中的操作并处理回复:


 [WKInterfaceController openParentApplication:@{@"action":@"toggleStatus"} reply:^(NSDictionary *replyInfo, NSError *error) {
    [logger trace:@"toggleStatus reply"];
    [self updateUIFromHost:replyInfo];
}];

在主机应用程序中, 我们可以访问传递的 userInfo NSDictionary,我们可以相应地对其做出响应。例如,在下面的代码中,我在 userInfo 实例上设置了一个字符串值,并根据该字符串的值采取适当的操作。


 [WKInterfaceController openParentApplication:@{@"action":@"toggleStatus"} reply:^(NSDictionary *replyInfo, NSError *error) {
    [logger trace:@"toggleStatus reply"];
    [self updateUIFromHost:replyInfo];
}];

这涵盖了“拉”场景,如果你想从你的 WatchKit 扩展调用你的主机应用程序中的操作,然后在 WatchKit 扩展中处理返回的响应以相应地更新你的 Apple Watch UI,这是很好的。

“推”场景呢?前面的场景仅涵盖源自 WatchKit 扩展内部的请求。如果你有一个进程在你的主机应用程序中运行,并且有更新你想在没有原始请求的情况下推送到 WatchKit 扩展,会发生什么?

没有共享内存,也不是共享进程,所以无论是 NSNotificationCenter 还是直接调用方法都行不通。但是,您*可以*使用 Darwin 通知(通过使用 CFNotificationCenter 在不同的进程中工作)。这支持跨进程的近实时交互,并且您可以在进程之间共享数据作为基于 CFdictionary 对象的属性。您还可以使用 访问组 共享大量数据,并使用 CFNotificationCenter 实现通知单独的进程。

注意:CFNotificationCenter 是 C 语法,不是 Objective-C 语法。

首先,您需要订阅 WatchKitExtension 中的 通知。注意静态 id 实例“staticSelf”……您稍后在从 C 通知回调调用 Objective-C 方法时将需要它。


 [WKInterfaceController openParentApplication:@{@"action":@"toggleStatus"} reply:^(NSDictionary *replyInfo, NSError *error) {
    [logger trace:@"toggleStatus reply"];
    [self updateUIFromHost:replyInfo];
}];

在您的主机应用程序中, 您可以调用 CFNotificationCenterPostNotification 来调用 Darwin 通知。


 [WKInterfaceController openParentApplication:@{@"action":@"toggleStatus"} reply:^(NSDictionary *replyInfo, NSError *error) {
    [logger trace:@"toggleStatus reply"];
    [self updateUIFromHost:replyInfo];
}];

然后 在 WatchKit 扩展中 ,处理通知,并相应地更新您的 WatchKit 扩展。


 [WKInterfaceController openParentApplication:@{@"action":@"toggleStatus"} reply:^(NSDictionary *replyInfo, NSError *error) {
    [logger trace:@"toggleStatus reply"];
    [self updateUIFromHost:replyInfo];
}];

我们现在已经介绍了您可以在主机应用程序中*从* WatchKit 扩展请求数据或操作的场景,以及如何将数据从主机应用程序推送到 WatchKit 扩展。

现在,如果有一个库封装了其中的一些内容,并使开发人员更容易使用呢?当我在上一篇文章中编写应用程序时,我使用了上述方法。然而,我最近偶然发现了开源 MMWormhole ,它包装了 Darwin Notifications 方法(上图)以便于使用。我很确定我会在我的下一个 WatchKit 应用程序中使用它。