iOS进阶3-WatchOS App 开发

187 阅读6分钟

WatchOS App 开发指南:从项目创建到数据交互与自主网络请求

watch 在IOT的使用中还是非常广泛的,刚好最近项目需要开发watch,这边简单记录一下。Apple Watch 已成为许多应用生态中不可或缺的一环,它为用户提供手腕上的便捷体验。开发一个功能完善的 WatchOS App,需要理解其独特的应用架构、与 iPhone 的通信机制以及独立运行的能力。本文将手把手带你完成从零开始创建 WatchOS App,并深入探讨如何实现设备间数据交互和自主网络请求。

一、 如何在现有iOS项目中新建WatchOS Target

为现有 iOS 项目添加 WatchOS 功能的第一步是创建相应的 Target。

在 Xcode 中,依次选择 ​File > New > Target...​,在弹出的模板选择器中,找到并选择 ​watchOS​ 选项卡下的 ​WatchKit App。点击下一步后,系统会提示你进行配置

这里需要注意几个关键选项:

  • Interface Controller​:选择故事板(Storyboard)或 SwiftUI 作为界面构建方式。
  • Include Notification Scene​:如果应用需要支持通知,请勾选此项。
  • Include Complication​:如果希望应用能在手表表盘显示复杂功能,请勾选此项。

完成配置后,Xcode 会为你的项目创建两个新的 Target​:

  1. WatchKit App​:这个 Target 主要包含用户界面资源,例如故事板文件、图片资源、资源文件等。它本身不包含任何代码。
  2. WatchKit Extension​:这里是 Watch App 的“大脑”,包含所有的业务逻辑代码、网络请求代码以及用于驱动界面的 WKInterfaceController 或 SwiftUI 视图。
组件所在位置主要职责
WatchKit AppApple Watch负责界面展示,包含故事板、图片等静态资源。
WatchKit ExtensionApple Watch (WatchOS 2+)负责业务逻辑、数据处理和网络通信。

重要提示​:确保 WatchKit App 和 WatchKit Extension 这两个 Target 的 ​Deployment Target​ 版本号与主 iOS 项目保持一致,以避免潜在的编译错误。

二、 WatchOS 与 iPhone 的数据交互

WatchOS App 与配对的 iPhone App 之间的无缝协作是其核心体验。Apple 提供的 ​WatchConnectivity​ 框架是实现这一目标的官方推荐方案,它支持在前台和后台进行双向通信。

初始化和激活会话

在任何通信发生之前,必须在 iPhone App 和 WatchKit Extension 中分别初始化并激活 WCSession

在 iPhone 端代码如下:​

swift
复制
import WatchConnectivity

class PhoneSessionManager: NSObject, WCSessionDelegate {
    func setupSession() {
        if WCSession.isSupported() {
            let session = WCSession.default
            session.delegate = self
            session.activate()
        }
    }
    
    // 实现 WCSessionDelegate 方法...
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if activationState == .activated {
            print("iPhone 端会话已激活")
        }
    }
}

在 WatchOS 端代码如下:​

swift
复制
import WatchConnectivity

class WatchSessionManager: NSObject, WCSessionDelegate {
    func setupSession() {
        // WatchOS 一定支持 WatchConnectivity,无需检查 isSupported
        let session = WCSession.default
        session.delegate = self
        session.activate()
    }
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if activationState == .activated {
            print("Watch 端会话已激活")
        }
    }
}

选择合适的数据传输方式

WatchConnectivity 提供了多种传输方式,适用于不同场景,核心区别如下表所示:

方法特性适用场景
sendMessage前台、实时、高优先级、需要对方应用处于活动状态。需要立即响应的操作,如触发一个即时动作。
updateApplicationContext后台、覆盖式传输,只保留最新数据。同步应用的最新状态,如用户设置、应用当前视图。
transferUserInfo后台、队列式传输,保证所有数据按顺序送达。需要可靠传输的历史数据记录,如健身记录、日志。
transferFile后台传输文件,并可携带元数据(metadata)。传输图片、音频等大型文件。
1. 前台实时消息(Send Message)

这种方式适用于需要立即得到响应的场景。

Watch 端发送消息示例:​

swift
复制
let message = ["command": "refreshData"]
WCSession.default.sendMessage(message, replyHandler: { reply in
    // 处理来自 iPhone 的回复
    DispatchQueue.main.async {
        self.updateUI(with: reply)
    }
}, errorHandler: { error in
    print("发送错误: (error.localizedDescription)")
})

iPhone 端接收并回复消息示例:​

swift
复制
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
    if message["command"] as? String == "refreshData" {
        // 获取最新数据
        let latestData = fetchData()
        // 将数据回复给 Watch
        replyHandler(["data": latestData])
    }
}
2. 后台应用上下文(Application Context)

当你只关心最新状态时,使用此方式。新的数据会覆盖旧数据。

iPhone 端更新上下文示例:​

swift
复制
do {
    let context = ["currentScore": "9500", "activeLevel": "5"]
    try WCSession.default.updateApplicationContext(context)
} catch {
    print("更新应用上下文失败: (error)")
}

Watch 端接收上下文示例:​

swift
复制
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
    if let score = applicationContext["currentScore"] as? String {
        DispatchQueue.main.async {
            self.scoreLabel.setText(score)
        }
    }
}

三、 WatchOS App 的自主网络请求

从 ​WatchOS 2​ 开始,WatchKit Extension 可以直接使用 NSURLSession 进行网络请求,这意味着 Watch App 不再需要完全依赖 iPhone 获取网络数据。这大大提升了应用的独立性和响应速度。

直接使用 URLSession

这是一个基本的 GET 请求示例:

swift
复制
func fetchDataDirectly() {
    guard let url = URL(string: "https://api.example.com/data") else { return }
    
    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            print("网络请求错误: (error.localizedDescription)")
            return
        }
        
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
            print("服务器返回错误")
            return
        }
        
        if let data = data {
            // 解析数据并更新 UI
            DispatchQueue.main.async {
                self.handleData(data)
            }
        }
    }
    task.resume()
}

使用第三方库(如 Alamofire)

像 Alamofire 这样的流行网络库也支持 WatchOS,可以让代码更简洁、易读。

首先,在 WatchKit Extension 的 Target 的 Podfile 或 Package.swift 中添加对 Alamofire 的依赖。

使用 Alamofire 的请求示例:​

swift
复制
import Alamofire

AF.request("https://api.example.com/data")
   .validate()
   .responseJSON { response in
        switch response.result {
        case .success(let value):
            print("JSON 数据: (value)")
            DispatchQueue.main.async {
                // 更新 Watch App 的 UI
            }
        case .failure(let error):
            print("请求错误: (error.localizedDescription)")
        }
   }

重要注意事项

  1. 网络条件​:Watch 的网络连接可能通过蓝牙连接的 iPhone、Wi-Fi 或蜂窝网络(蜂窝网络版手表)实现。务必考虑网络不稳定和延迟的情况,做好错误处理和超时设置。
  2. 性能与电量​:手表设备资源有限。网络请求应精简高效,只获取必要数据,避免频繁和大数据量的请求以节省电量。
  3. 后台处理​:WatchOS 对后台任务的限制非常严格。如果需要后台刷新数据,请研究 WKBackgroundRefreshTask 等相关 API。

四、 实战技巧与常见问题

共享数据模型

对于 iOS 和 WatchOS 都需要使用的数据模型(例如从 JSON 解析的用户信息、设置项等),最佳实践是将其放在一个共享的 Swift Package 或共享文件组中,并确保它们被同时添加到 iOS 和 WatchKit Extension 的 Target 中。这保证了数据模型的一致性,避免了重复代码。

调试技巧

调试 Watch App 有时需要同时观察 iPhone 和 Watch 两端的日志。你可以在 Xcode 的菜单栏中选择 ​Debug > Attach to Process,然后选择你的 iOS App 进程,这样就可以同时调试两个 Target 的代码。

总结

开发一个成熟的 WatchOS App 涉及三个关键环节:正确创建项目结构、利用 WatchConnectivity 框架实现高效可靠的数据交互,以及根据场景选择合适的网络请求方案以保障独立运行体验。WatchOS 开发虽然有其特殊性,但一旦掌握了这些核心概念,你就能为用户打造出在手腕上流畅运行的出色应用。

希望这篇指南对你有帮助!如果你在开发过程中遇到具体问题,欢迎留言讨论。