暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

一次 Apple Watch App 开发经历

巴韭特锁螺丝 2022-10-03
196

前言:
随着现在 Apple
 生态圈的发展,越来越多的 App
 会把自己的简化版从 iOS
 迁移至 WatchOS
(支付宝、微信、手Q、头条、QQ音乐、网易云音乐等等,都有Watch
App
)。
于是,我也是第一次尝试了把我们的组的 iOS App
 迁移至 Apple Watch

从调研到实现,大概花了一周的时间。也踩了一些坑,记录一下。


一、Apple Watch:

Apple Watch
是苹果公司主打 “健康” 概念的智能手表。
于2014年发布第一代Apple Watch 1
,截至2020年,已发布Apple Watch 5

Apple Watch App
分为两种:

  • Watch App for iOS App:从iOS
    迁移过来的Watch App
    ,可与iOS App
    通信。

  • Watch App:独立的Watch App
    ,可独立安装在Apple Watch
    上。

大部分是第一种,Watch App for iOS App
。本文也是以第一种情况举例。


准备工作:

新建一个watchOS
target

新建target

这时,会出现两个target:Apple Watch
Apple Watch Extension

image

注意:在 WatchOS
 中,无法像 iOS
 那样依赖 UIKit
 写出各种复杂的界面。目前,只能依赖 storyboard
 搭建出一些简单的UI界面与界面跳转逻辑。

二、与iOS的主要区别:

  1. 只能用storyboard
    拖拽相应控件,搭建基本UI。

  2. 简单布局,默认是垂直布局。可通过嵌套Group
    来完成纵向布局需求。

  3. 界面之间的传值,需要依赖contextForSegue
    方法。
    storyboard
    中设置segueIdentifier

    同时在下一级controller的awake(withContext context: Any?)
    方法接收解析context

override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? {
if segueIdentifier == "" {
// ...
return "A"
} else {
// ...
return "B"
}
}

override func contextForSegue(withIdentifier segueIdentifier: String, in table: WKInterfaceTable, rowIndex: Int) -> Any? {
if segueIdentifier == "" {
if rowIndex == 0 {
return "A"
} else {
return "B"
}
} else {
return "C"
}
}

--------------------------------------------

// 下一级controller中,通过context对象接收。
override func awake(withContext context: Any?) {
super.awake(withContext: context)

let item = context as? String // 上一级传递的数据
print(item)

// ...
}

三、iOS与WatchOS的通信:

Apple在 WatchOS 2.0
后发布了 WatchConnectivity
框架,用于iOS
WatchOS
之间的通信。

1.iOS端实现:

实现一个单例WatchManager
。用于给Watch
端发消息、接收Watch
的消息。
App启动后,在合适时机调用startSession
。初始化WCSession
回话。

import UIKit
import WatchConnectivity

class WatchManager: NSObject, WCSessionDelegate {

static let manager = WatchManager()

var session: WCSession?

private override init() {
super.init()
}

func startSession() {
if WCSession.isSupported() {
session = WCSession.default
session?.delegate = self
self.session?.activate()
}
}

func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
print(session)
}

func sessionDidBecomeInactive(_ session: WCSession) {
print("🏡sessionDidBecomeInactive")
}

func sessionDidDeactivate(_ session: WCSession) {
print("🏡sessionDidDeactivate")
}
}

同时,在需要给手表发消息的地方调用sendMessage
sendMessageData
transferFile
方法。
可以传递 Dictionary
Data
file
类型的数据。

注意:这里有个坑,sendMessage
 方法的 replyHandler
errorHandler
参数不能直接传nil
,不然消息可能会发不出去。

if TDWatchManager.manager.session?.isReachable == true { //判断是否可达
TDWatchManager.manager.session?.sendMessage(["key": "value"], replyHandler: { (dict) in
print(dict)
}, errorHandler: { (error) in
print(error)
})
}

2.Watch端实现:

同样,实现一个单例WatchSessionManager
。用于接收iOS
端的消息,给iOS
端发消息。
在App启动后,调用startSession
,初始化session
对象。

import WatchKit
import WatchConnectivity

class WatchSessionManager: NSObject, WCSessionDelegate {
static let manager = WatchSessionManager()

var session: WCSession?

private override init() {
super.init()
}

func startSession() {
if WCSession.isSupported() {
session = WCSession.default
session?.delegate = self
self.session?.activate()
}
}

// 数据来源:
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
print("收到iPhone端的userInfo")
}


func session(_ session: WCSession, didReceive file: WCSessionFile) {
print("收到iPhone端的file")
}

func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
print("收到iPhone端的message")
}

func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
print(session)
}
}

发消息、接收消息也与iOS
端实现一致。
总结来说,就是通过WatchManager
单例通信,并通过代理回调接收消息。

四、一些“坑”与解决方案:

  1. 使用WatchConnectivity
    通信,需要iOS App
    Watch App
    端同时存活,同时处于Reachable
    状态。
    只要一端不在线,就无法通信。
    如果对数据的实时性有要求,Watch
    端就不能依赖iOS
    端的数据了。

  2. Apple Watch 4
    及以下的设备是32
    位的硬件与系统,无法解析64
    位的数据。(Apple Watch 5
    开始是64
    位的硬件与系统)

解决方案:

  1. 对于问题一,好在Watch
    端可连接WiFi
    ,支持NSURLSession

    可以使用AFNetworking
    /Alamofire
    主动发动请求。这样就保证的数据的实时性。
    但请求里的登录态(token
    校验等等)怎么办呢?
    目前的方案是,先通过WatchConnectivity
    通信从iOS端获取用户数据(token
    等等),并缓存在Watch本地用于请求。(为了防止token
    失效等问题,只要iOS
    端和Watch
    端同时在线时,更新并缓存最新的token
    。)

  2. 对于问题二,如果请求里含有64
    位数据(比如Int64
    ),那么可能需要服务端配合处理一下了。
    Watch
    端的数据不要包含64
    位的数据。
    目前没想到很好的解决方法,毕竟是32
    位的硬件设备。

五、特殊需求:Apple Watch生成二维码

这里感谢:《QRCode.generate()! —— BiliBili》这篇博客。
博主推荐了一个用Swift
写的强大的二维码三方库:EFQRCode。
支持: iOS, macOS, watchOS and tvOS.

  • 导入:pod 'EFQRCode/watchOS'

  • 使用:在Watch
    端生成二维码。

let cgImage = EFQRCode.generate(content: "https://github.com/EFPrefix/EFQRCode")
if let cgImage = cgImage {
ImageView.setImage(UIImage(cgImage: cgImage))
}

六、Watch相关参考学习资料

官方文档 
Apple Watch开发入门(系列) 
Apple Watch开发(系列)

    免责声明:本文内容来自简书>作者 : 齐舞647,遵循CC 4.0 BY-SA版权协议,载请附上原文出处链接及本声明。
    本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行可。
    原文链接:https://www.jianshu.com/p/eea9e7da144c
    如有涉及到侵权,请联系,将立即予以删除处理。
    在此特别鸣谢:简书博主>齐舞647的创作。
    本文发布已获原作者授权转载。
    此篇文章的所有版权归原作者所有,与本公众号无关,商业转载建议请联系原作者获得授权,非商业转载请注明出处。


    文章转载自巴韭特锁螺丝,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论