WatchOS开发教程之四: Watch与 iPhone的通信和数据共享

WatchOS 开发教程系列文章:
WatchOS开发教程之一: Watch App架构及生命周期
WatchOS开发教程之二: 布局适配和系统Icon设计尺寸
WatchOS开发教程之三: 导航方式和控件详解
WatchOS开发教程之四: Watch与 iPhone的通信和数据共享
WatchOS开发教程之五: 通知功能开发
WatchOS开发教程之六: 表盘功能开发


Apple Watch与iPhone之间的通信

在第一篇文章Watch App架构及生命周期的最后, 我们提到过: 避免长时间运行的任务。由于与 Watch App的交互通常很简短, 因此在长时间运行的任务完成之前, 可能会暂停 WatchKit Extension。 执行任何长时间运行任务的最佳解决方案是在 iOS App中执行该任务, 然后将数据传输给 Apple Watch。

所以, 今天来说一说Apple Watch与iPhone之间的通信是如何实现的。

WatchConnectivity框架

在 WatchOS中有个WatchConnectivity框架, 是专门负责 WatchOS与 iOS之间的通信的。使用Connectivity框架在 WatchKit Extension和 iOS App之间进行通信。该框架提供了两个进程之间的双向通信,并允许在前台或后台进行数据和文件的传输。

Connectivity框架提供了几种在 iOS 和 WatchKit Extension之间发送数据的选项, 每个选项都用于不同的用途。大多数选项在后台执行单向数据传输,而且是提供更新的便捷方式。前台传输让你的应用立即发送消息并等待回复。

对于大多数类型的传输,您提供一个NSDictionary包含要发送的数据的对象。字典的键和值必须都是属性列表类型,因为数据必须序列化并以无线方式发送。属性列表类型是指Foundation框架中的NSNumbeNSStringNSArrayNSDictionaryBoolNSDateNSData等数据类型。如果需要包含非属性列表类型的类型,请将它们打包到NSData对象中,或者在发送之前将它们写入文件。此外,您发送的词典应该是紧凑的,并且只包含您需要的数据。保持字典较小可确保它们快速传输,并且不会在两台设备上消耗太多电量。

WCSession

WatchConnectivity框架中主要是通过WCSession类进行数据传输的。来看下WCSession这个类, 它有一个default单例。default session用于在两个对应应用程序(即 iOS App及 WatchKit Extension)之间进行通信。Session提供了发送,接收和跟踪状态的方法。

您的 iOS App和 WatchOS App必须在执行期间的某个时刻创建和配置此类的实例。当两个会话对象都处于激活状态时,这两个进程可以通过发送消息立即进行通信。当只有一个Session处于激活状态时,Session仍可以发送更新和传输文件,但这些传输在后台机会性地发生。

Session的配置和激活

在尝试发送消息或获取有关连接状态的信息之前,必须配置并激活Session。 在激活Session之前,可需要先进行一个检查当前 iOS设备是否支持Connectivity框架, 方法就是调用isSupported()方法。

在 iOS App中检查是否支持Connectivity框架并激活Session的代码如下:

func configureWCSession() {if #available(iOS 9.0, *) {// Some properties can be checked only for iOS Device// WCSession.default.isPaired// WCSession.default.isWatchAppInstalled// WCSession.default.isComplicationEnabledif WCSession.isSupported() {let session = WCSession.defaultsession.delegate = selfsession.activate()} else {// Current iOS device dot not support session}} else {// The version of system is not available}
}

WCSession类中, 还有一些属性是只能在 iOS App中使用的。比如, isPaired, isWatchAppInstalled, isComplicationEnabled, remainingComplicationUserInfoTransfers, watchDirectoryURL。这些都是仅仅在 iOS App中可用的, 都是标示当前设备的某些状态的。所以, 我们要在通信前利用好这些属性。

在激活WatchKit Extension的Session前, 不必检查是否支持Connectivity框架, 因为 WatchOS一定支持Connectivity框架。所以, 在 WatchKit Extension中Session的配置和激活就相对简单一些:

func configureWCSession() {// Don't need to check isSupport state, because session is always available on WatchOS// if WCSession.isSupported() {}let session = WCSession.defaultsession.delegate = selfsession.activate()
}

选择合适的通信方式

WatchKit Extension与 iPhone间的通信方式有很多种, 可以分为前台实时传输和后台不定时传输两大传输类型。前台传输, 是实时传输, 消息字典传输和消息数据传输。后台传输又分为覆盖式传输, 队列式传输。队列式传输又分为字典传输, 文件传输, 表盘数据传输。一张图把这一切说清楚:

后台传输

后台传输是异步执行的, 当发送方的应用退出时,后台传输将会继续。对应的接收方应用不需要运行也可以继续进行后台传输。并且在 WatchKit Extension与 iPhone进行传输的方法中, 所有的后台传输都是不定时传输。不定时意味着, 数据不一定会立即传输, 而是系统将在适当的时间传输内容。当然包括上面所说的在应用程序退出之后发生, 甚至是在双方应用都不运行时发生。接收方没有运行但传输成功了, 下次启动时将会触发相应的代理方法。

后台覆盖式传输

后台传输中覆盖式的传输意味着, 当你进行数据传输时, 如果第一次发送的数据还没有送出去, 在此时进行第二次数据传递, 将会覆盖第一次的数据。这时数据接收方接收的数据只会有第二次的, 第一次的数据会丢失。

Connectivity框架的通信方法中, 后台覆盖式传输只有一个方法, 在 Objective-C中就是updateApplicationContext:error:方法, 在 Swift中函数如下:

open func updateApplicationContext(_ applicationContext: [String : Any]) throws

一般使用该方法将最近的状态信息传递给对方, 且只有在Session处于激活状态时才能调用此方法, 系统将会在适当的时间传输内容。使用此方法传输后, 发送的数据会存储在applicationContext属性中, 而最新接收的数据会存储在receivedApplicationContext属性中。

接收方可以遵从 WCSessionDelegate的协议, 在这些代理方法中, 有一个是与上面的方法成对存在的。接收方若实现了下面的代理方法, 当数据发送方在调用上面的方法后, 将会触发它们:

optional public func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any])

后台队列式传输

后台传输中队列式的传输意味着, 后一次的传输不会覆盖前一次所传输的数据。系统会把所有的数据按照次序进行发送。在Connectivity框架的通信方法中, 后台队列式有三个方法, 后台队列式字典传输, 后台队列式文件传输, 后台队列式表盘数据传输。

后台队列式字典传输

在 Objective-C中后台队列式字典传输的方法是transferUserInfo:, 在 Swift中函数如下:

open func transferUserInfo(_ userInfo: [String : Any] = [:]) -> WCSessionUserInfoTransfer

此方法可以传输一个字典, 且只有在Session处于激活状态时才能调用此方法。系统将userInfo字典按序排入队列, 并在适当的时候将其传输到接收方应用中。你还可以通过outstandingUserInfoTransfers属性来获取仍在传输中(即未被接收方取消, 失败或已接收)的userInfo数组。

接收方可以遵从 WCSessionDelegate的协议, 在这些代理方法中, 有两个是与上面的方法成对存在的。接收方若实现了下面的代理方法, 当数据发送方在调用上面的方法后, 将会触发它们:

optional public func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:])optional public func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?)

后台队列式文件传输

在 Objective-C中后台队列式文件传输的方法是transferFile:metadata:, 在 Swift中函数如下:

open func transferFile(_ file: URL, metadata: [String : Any]?) -> WCSessionFileTransfer

此方法可以传输一个文件和一个可选字典, 且只有在Session处于激活状态时才能调用此方法。你还可以通过outstandingFileTransfers属性来获取仍在传输中(即未被接收方取消, 失败或已接收)的userInfo数组。

接收方可以遵从 WCSessionDelegate的协议, 在这些代理方法中, 有两个是与上面的方法成对存在的。接收方若实现了下面的代理方法, 当数据发送方在调用上面的方法后, 将会触发它们:

optional public func session(_ session: WCSession, didReceive file: WCSessionFile)optional public func session(_ session: WCSession, didFinish fileTransfer: WCSessionFileTransfer, error: Error?)

这里需要注意的是, 接收到的文件存放在本地临时路径Documents/Inbox/中, 代理方法session(_ session: WCSession, didReceive file: WCSessionFile)结束后系统会将文件删除。所以你需要接收到文件后, 立即对其进行读取或者移动。

后台队列式表盘数据传输

在 Objective-C中后台队列式表盘数据传输的方法是transferCurrentComplicationUserInfo:, 在 Swift中函数如下:

open func transferCurrentComplicationUserInfo(_ userInfo: [String : Any] = [:]) -> WCSessionUserInfoTransfer

此方法涉及到 WatchOS的表盘功能也就是Complication功能, 且只适用于 iPhone向 WatchKit Extension发送表盘功能相关的数据。此方法将包含表盘功能的最新信息的字典userInfo排入队列中。并且只有在Session处于激活状态时才能调用此方法。

与之相关的属性有remainingComplicationUserInfoTransfers, 它标示这transferCurrentComplicationUserInfo:方法的剩余调用次数。在系统开始将表盘userInfo作为常规userInfo传输之前。 如果此属性为0,则表盘userInfo将作为常规userInfo传输。 当 Watch应用未启用表盘功能时, 其计数也为0。

如果启用了表盘功能, 系统将立即尝试向 WatchKit Extension传输此userInfo, 且传输为高优先级。一旦收到当前的表盘功能的userInfo, 系统将在后台启动 WatchKit Extension并允许其更新并表盘内容。如果当前用户信息无法传输(即设备断开连接, 超出后台启动预算等), 它将在outstandingUserInfoTransfers队列中等待, 直到下一个合适的时间。

需要注意的是, 在outstandingUserInfoTransfers队列中只能有一个当前的表盘的userInfo。如果当前表盘userInfo还在队列当中(等待传输), 并且再次传输了一个新的userInfo, 则新userInfo将被标记为当前需要传输的userInfo。而先前的userInfo将被取消标记, 那么无论如何它都将一直存在于outstandingUserInfoTransfers队列中了。

表盘功能传输中接收方(即 Watch端)可以实现的代理与后台队列式字典传输的代理相同。

前台传输

在 WatchKit Extension与 iPhone进行传输的方法中, 前台传输是实时的, 且是队列式的传输方式。具体有两种方法, 一种是传输消息字典, 另一种是传输消息数据。

前台消息字典传输

在 Objective-C中前台消息字典传输的方法是sendMessage:replyHandler:errorHandler:, 在 Swift中函数如下:

open func sendMessage(_ message: [String : Any], replyHandler: (([String : Any]) -> Swift.Void)?, errorHandler: ((Error) -> Swift.Void)? = nil)

此方法传入一个消息字典, 一个处理接收方回复的block, 以及一个错误处理block。消息的传递是异步的、高优先级的, 且只有在会话处于活动状态时才能调用此方法。如果指定了处理接收方回复的block, 则该block也会在后台线程上异步执行。

需要注意的是, 从 WatchKit Extension激活并运行时调用此方法会在后台唤醒相应的 iOS App并使其可访问。但若从 iOS App调用此方法则不会唤醒相应的WatchKit Extension。如果调用此方法时接收方无法访问(即isReachable是 false), 则会执行errorHandlerblock并显示相应的错误。

那么isReachable什么时候是true呢? 对于 WatchKit Extension来说, iOS设备在范围内, 因此可以进行通信并且 WatchKit Extension在前台运行,或者在后台运行时具有高优先级(例如, 在锻炼会话期间或当表盘加载其初始时间轴数据时); 对于 iOS来说, 配对且激活的 Apple Watch在范围内, 相应的WatchKit Extension正在运行。只要这样isReachable属性才会为true。

而且当传输的消息字典中包含非属性列表数据类型, 也会调用errorHandlerblock。其他的类型数据应该用你下面的方法来进行传输。

前台消息数据传输

在 Objective-C中前台消息字典传输的方法是sendMessageData:replyHandler:errorHandler:, 在 Swift中函数如下:

open func sendMessageData(_ data: Data, replyHandler: ((Data) -> Swift.Void)?, errorHandler: ((Error) -> Swift.Void)? = nil)

此方法与消息字典传输的方法的区别在于所传输的主体内容为Data类型。包含非属性列表数据类型的传输, 就需要使用此方法了, 否则用上面方法将会报错。

传输数据处理

当你选择了合适的方式进行数据通信后, 就是处理这些接收到的数据和处理接收方回复了。核心就是在WCSessionDelegate中不同传输方式对应的不同代理方法中去处理。其实, 这些上面每个方法中已经详细说过了, 但为了知识的结构这里还是有必要提一下的, 详细的这里不赘述了。

代码描述

以前台消息字典传输为例, 由 WatchKit Extension向 iOS App发送消息。 WatchKit Extension中代码如下:

if !WCSession.default.isReachable {let action = WKAlertAction(title: "OK", style: .default) {print("OK")}presentAlert(withTitle: "Failed", message: "Apple Watch is not reachable.", preferredStyle: .alert, actions: [action])return
} else {// The counterpart is not available for living messageing
}let date = Date(timeIntervalSinceNow: 0.0)
let message = ["title": "Apple send a messge to iPhone", "watchMessage": "The Date is \(date.description)"]
WCSession.default.sendMessage(message, replyHandler: { (replyMessage) inprint(replyMessage)DispatchQueue.main.sync {self.contentLabel.setText(replyMessage["replyContent"] as? String)}
}) { (error) inprint(error.localizedDescription)
}

WatchKit Extension中实现对应代理方法, 以处理 iOS App发回的回复数据:

func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {DispatchQueue.main.sync {contentLabel.setText(message["iPhoneMessage"] as? String)}
}

iOS App也应实现对应代理, 以处理接收到的数据:

@available(iOS 9.0, *)
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {print(message)replyHandler(["title": "received successfully", "replyContent": "This is a reply from iPhone"])
}

数据共享

Watch App和 WatchKit Extension间数据共享

在运行时可以使用共享App Group在 Watch App和 WatchKit Extension之间共享媒体文件。 App Group创建一个多个进程可以访问的安全容器。 通常每个进程都在自己的沙箱环境中运行, 但是App Group允许两个进程共享一个公共目录。

如何使用共享App Group

1.在Xcode中打开项目的Capabilities选项卡。
2.启用App Group功能。这将会添加一个entitlement file到指定的Target,并将一个唯一标识的App Group添加到该文件中。

3.且需要注意Watch App和 WatchKit Extension的 Target必须都启用相同的App Group
4.访问其中内容时, 使用NSFileManagercontainerURLForSecurityApplicationGroupIdentifier:方法取得文件的URL。

文件存储

WatchKit Extension的存储目录与iOS App的存储目录具有相同的基本结构。将用户数据和其他关键信息放在Documents目录中。如果将文件放在Caches目录中, 磁盘空间量较低时系统会删除它们。

数据备份

Apple Watch不会自动备份WatchKit Extension保存的文件。如果需要备份 Watch App中的数据, 则必须将该数据明确传输回iOS App并将其保存在那里。

iCloud

从 WatchOS 3开始, WatchKit Extension可以直接与CloudKit和其他iCloud技术进行通信。

相关资料:
Sharing Data
WatchOS 开发教程源码:Watch-App-Sampler

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2219650.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Kali Linux 秘籍 第九章 无线攻击

第九章 无线攻击 作者:Willie L. Pritchett, David De Smet 译者:飞龙 协议:CC BY-NC-SA 4.0 简介 当今,无线网络随处可见。由于用户四处奔走,插入以太网网线来获取互联网访问的方式非常不方便。无线网络为了使用便利…

python3遍历目录查找文件

一直有一部分软件,他们的主要功能就是方便用户查找本地文件位置。python当然也可以完成这项功能,所以我写了一个简短的代码。 写完发现,python真的是一门简洁的语言啊! 我完成这个功能主要就是用了os模块的功能,直接…

WinSvr:在 Windows Server 中启用无线连接

默认情况下,所有 Windows Server 2022/2019/2016/2012R2 版本都禁用无线 (Wi-Fi) 支持。如果将 Wi-Fi 网络适配器(USB 或 PCI)插入运行 Windows Server 的主机,则无法在控制面板中启用它。本文将在这篇简短的说明中展示如何在 Windows Server 上启用无线支持。 注意,在 W…

【运维笔记】Docker 部署Kibana-7.4.0(在线Docker版)

Docker 部署Kibana-7.4.0(在线Docker版) 一、准备工作: Centos 7.5 安装 Docker-24.0.6 详细步骤(避坑版): https://blog.csdn.net/seesun2012/article/details/133674191注意1:本文的命令使用…

短视频账号矩阵系统源码saas===独立部署

前言: 短视频账号矩阵是指在不同的短视频平台上,一个个人或企业所拥有的账号数量和分布情况。由于不同的短视频平台受众人群和内容类型等因素不同,因此拥有更多账号可以在更广泛的受众中传播内容,提高曝光度和流量。短视频账号矩阵…

管理类联考——逻辑——真题篇——按知识分类——第十章 数学相关

第十章 数学相关 第一节 集合 真题(2010-53)-数学相关-集合-画饼集能力-朴素逻辑 53.参加某国际学术研讨会的 60 名学者中,亚裔学者 31 人,博士 33 人,非亚裔学者中无博士学位的 4 人。根据上述陈述,参…

2017年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——解析版

🏠个人主页:fo安方的博客✨ 💂个人简历:大家好,我是fo安方,考取过HCIE Cloud Computing、CCIE Security、CISP、RHCE、CCNP RS、PEST 3等证书。🐳 💕兴趣爱好:b站天天刷&…

爬虫小白系列01期: 从李白杜甫,来看爬虫本质 、 浏览器访问网页原理 、 请求头的概念

众所周知,爬虫的本质是,模拟浏览器打开网页,获取网页中我们需要的那部分数据。 那首先我们应该清楚,普通一般浏览器打开网页的流程和原理是怎样的? 根据生活经验,我们使用浏览器打开网页的步骤一般是这样…

神犇营my0001:春晓

本题来源于神犇营 题目: [my0001] 唐代诗人孟浩然所作的《春晓》是一首家喻户晓的诗,但是校园里更流行改编版的《春晓》。 春眠不觉晓, 处处蚊子咬。 夜里嗡嗡声, 脓包知多少。 现在我们要用刚才所学的知识来输出这首诗的前两句。首先在右边的输入C++程序的基本框架…

世界十大名诗

世界十大名诗 时间:2011-01-07 来源:网络 点击:318次 When You Are Old by William Butler Yeats (1865-1939) WHEN you are old and gray and full of sleep, And nodding by the fire, take down this book, And slowly re…

9月火气大,能认真写代码么?

不羡鸳鸯不羡仙,一行代码调半天。原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。 我在风中藏把刀,斩尽世间秋色。 这句注定要流传千古的名诗,是xjjdo…

html语言登黄鹤楼,《中国诗词大会》命题专家方笑一, 揭秘千古名诗《登黄鹤楼》为何格律“不合格”...

楚天都市报10月26日讯(记者舒均 李辉 通讯员王红念 江萌)10月26日上午,华东师范大学古籍研究所教授、央视 《中国诗词大会》命题专家暨现场学术顾问方笑一做客“黄鹤大讲堂”,带来一场《古诗词与天下名楼》品评锦绣诗词的讲座,受到江城上百名…

NLP 自然语言处理实战

前言 自然语言处理 ( Natural Language Processing, NLP) 是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法,用于分析理解机器与人之间的交互,常用的领域有:实体识别、文…

软件行业与就业(导师主讲)

在企业软件应用的整体架构体系中,有一部分被称为中间件,那么什么叫中间件? 中间件(Middleware)是指位于操作系统和应用程序之间的一层软件层,它提供了一组工具和服务,用于简化和增强企业软件应用…

Docker修改阿里源

在一次安装rtmp推流服务时,总是无法下载源,估计是国外资源下载超时照成的,于是想到修改为国内源。 docker pull alfg/nginx-rtmp Using default tag: latest latest: Pulling from alfg/nginx-rtmp 530afca65e2e: Retrying in 7 seconds c20…

试卷模板 html,一年级语文试卷模板

一年级语文试卷模板 每个学期我们都要进行一次考试,为让大家能够考的更好,下面,小编为大家分享一年级语文试卷模板,希望对大家有所帮助! 一、选字填空,再把句子读一读。 元 园 原 圆 员 1.公(   )里的花好看极了…

Qt QGridLayout和QFormLayout案例分析

QGridLayout和QFormLayout是Qt中常用的布局管理器,可以用于在应用程序中设置控件的位置和大小。 QGridLayout网格布局(栅格布局) QGridLayout是一个网格布局管理器,可以将控件放置在一个二维网格中。在QGridLayout中,控件可以跨越多个行和列…

linux中安装RocketMQ以及dashboard

前提: 需要安装jdk8 上传下面的文件到服务器中 新建目录 mkdir rocketmq 将下载后的压缩包上传到阿里云服务器或者虚拟机中去,并解压 unzip rocketmq-all-4.9.2-bin-release.zip 配置环境变量 vim /etc/profile 配置内容: export NAM…

mysql面试题29:大表查询的优化方案

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一下大表查询的优化方案 以下是几种常见的大表优化方案: 分区&…

ubuntu显卡驱动掉了重新装

之前装好的Ubuntu显卡驱动因为系统内核自动升级(以防下次再次出现这种情况现在立刻马上关闭所有自动更新)输入nvidia-smi指令不再提示显卡信息,此时不需要重装系统,再次装显卡驱动即可。方法如下(此方法适用于之前装成…