Listify for WatchOS 开发笔记

January 5, 2021
ListifyiOS开发CloudKit

一年前 Listify 上线了 WatchOS 版本,随着 iOS 14 的发布我收到了很多关于 WatchOS 版本无法正常运行的评论,那么不如重构一下好了。

Listify for WatchOS 的功能很简单,App 主界面会展示用户的清单列表,点击任何一个项目则会展示清单的内容,再点击清单中的任何一项即可标记条目为完成或者未完成。 即使这个功能异常简单,但还是需要读取用户数据。

在官方文档 Keeping Your watchOS Content Up to Date 中解释的很清楚,如果需要读取用户数据,只有三种方法:

  • 直接连接服务器
    WatchOS 支持 App 直接使用 URLSession 发送请求获取数据
  • 使用 iCloud
    如果 App 使用了 CloudKit 储存数据,那么可以直接和 iOS 一样使用 iCloud 对云端数据进行访问,前提是用户在你的 App 上启用了 iCloud。
  • 直连 iPhone
    使用 Watch Connectivity 框架可以让Watch App 直接与 iPhone App 交换数据,前提是Watch App是一非独立的,如果用户的手表没有和 iPhone 配对或者没有连接,那么使用这个方法将无法传输数据。而前两种方式不会受到iPhone 配对的限制,Watch App 可以独立于 iPhone 存在。

一年前准备开发 Watch 应用的时候,我以为 Watch 应用可以像 Share Extension,Today Extension,WidgetKit 一样,使用 UserDefault 来交换数据,可惜不行,似乎在 WatchOS 1的时候是是支持的,后来被 Watch Connectivity 替代了。

Watch Connectivity

所以当初为了方便,选择了 Watch Connectivity 方案开发,可能是因为我是用的是很早之前就淘汰掉的 Apple Watch 2 的缘故,用户体验相当糟糕 ,Watch App 启动到能看见清单列表需要经历很长一段时间,并且经常会出现无法连接 iPhone 的情况,需要点击一下重试按钮才可以。但是这些问题在使用 watchOS 模拟器的时候都无法复现。
想要使用 Watch Connectivity,iOS app 需要实现 WCSessionDelegate,同时 Watch App 也要实现相应的 Delegate 以及用于表示一次会话的 WCSession。这使得整个 App 数据流准备变得异常痛苦,想要通过 WCSession 进行实时通讯,需要调用 WCSession 的 activate 方法,开发者需要在 Watch 应用启动的时候或者从后台返回的时候尝试去建立连接,也就是说在Watch App 生命周期的 awake 和 willActive 都需要尝试激活WCSession,并且WCSession也有很多状态,iPhone 可能与 Watch 建立了连接但是没有配对,这些都是需要考虑到的。使用 Watch Connectivity 让整个 Watch App 的每一个 Controller 中导出都是与WCSession的相关代码,我需要在每一个 Controller 里面都去判断 WCSession 的状态,并作出相应的处理。真正的业务代码反而没多少。

  1. override func willActivate() {
  2. super.willActivate()
  3. session.delegate = self
  4. if session.activationState != .activated {
  5. session.activate()
  6. }else{
  7. // 请求数据
  8. requestList()
  9. }
  10. }

更糟糕的是,App 没有启动的时候有一定几率是无法建立 WCSession 的,但有时候可以,全凭运气。在iPhone模拟机上不存在任何问题,即使手动把 iOS App 杀掉再进入 Watch App,依然可以稳定建立 WCSession,不知道是不是因为我的手表太老的原因。毕竟其他开发商的 Watch App 在我的手表上体验都是一样的差劲。

这也是因为我使用的姿势不太对,官方文档声明过使用 Watch Connectivity 可以用于传输文件之类的,并且不应该因为追求实时的数据传输而选择使用 Watch Connectivity。

Use this framework to transfer data between your iOS app and the WatchKit extension of a paired watchOS app. You can pass small amounts of data or entire files. You also use this framework to trigger an update to your watchOS app’s complication.
After initiating a transfer from your app, the system assumes responsibility for the transmission of any data. Most transfers happen in the background when the receiving app is inactive. When the app wakes up, it is notified of any data that arrived while it was inactive. Live communication is also possible when both apps are active.

iCloud

在经历了各种调试不出来以及iOS 14发布后不知道怎么突然变多的用户投诉之后,我知道是时候重构一下 Listify for WatchOS 了,使用 iCloud 一定是最好的方案。在 2019 年的时候 Listify 就支持了 iCloud 同步,可以让用户在iPhone 和 iPad 上无缝的同步数据。这次使用 iCloud 作为 Watch App 的方案也是水到渠成。
没有使用过CloudKit的同学可以看我之前写过的文章 Listify 的iCloud云同步功能开发笔记
iCloud 的接入没什么好说的,如果 iOS 主 App 已经使用了 CloudKit,那么在Watch App项目的Target中开启 CloudKit 即可直接使用了。需要注意的是 Watch App 上声明 iCloud 的 CKContainer时,不能再使用默认的CKContainer,而是应该在 CKContainer 构造的时候传入主 App 的 bundle identifier。

  1. private let db = CKContainer(identifier: "iCloud.it.xxxx.xxxx").privateCloudDatabase

使用了 iCloud 之后用户体验提升了很多。之后也没有用户再反应过 Watch App 不可用的问题了。

Comments

作者
July 21, 2018 at 10:52 am

There are no comments

keyboard_arrow_up