WatchConnectivity 是 Apple 提供的一个框架,允许 iOS 设备与 Apple Watch 之间进行通信和数据交换。它的主要作用是使得 iOS 应用 和 WatchOS 扩展 能够互相传递数据和消息。这对于实现 iPhone 和 Apple Watch 之间的同步和交互非常重要,尤其是对于需要双向通信的应用,如健康监测、通知、实时数据等。
首次出现在 iOS 8 和 WatchOS 2 中。它允许 iOS 应用与 WatchOS 应用之间交换数据和消息,例如传输文件、同步数据、发送实时消息等。通过 WatchConnectivity,让两者在设备之间保持通信和同步。
WatchConnectivity 的核心功能
1、即时消息传递:
可以从 iPhone 发送即时消息到 Apple Watch,或反之。适用于实时数据更新或交互。例如,更新界面、发送通知等。
if WCSession.default.isReachable {
WCSession.default.sendMessage(["key": "value"], replyHandler: { response in
// 处理响应
}, errorHandler: { error in
// 处理错误
})
}
2、数据传输:
可以在两个设备之间传输文件或应用数据。比如,将用户数据从 iPhone 同步到 Apple Watch,或将日志、图片等文件传输到另一个设备。
3、应用状态同步:
允许设备间同步应用状态,例如将 iOS 设备上的某个数据同步到 Apple Watch 或将 Watch 上的健康数据、提醒等同步到 iPhone。
4、背景数据传输:
即使应用处于后台,WatchConnectivity 也能传输数据。系统会在后台处理消息和数据同步,前提是设备仍然保持连接。
5、Live Activity 支持:
支持通过 Live Activity 更新实时信息。比如,可以从 iOS 向 Apple Watch 发送实时活动信息,例如计时器、位置等动态数据。
如何使用 WatchConnectivity
使用 WatchConnectivity 进行通信时,主要通过 WCSession 类来管理会话。需要在 iOS 应用 和 WatchOS 扩展中分别实现。
1、初始化 WCSession
在 iOS 和 WatchOS 应用中都需要初始化并激活 WCSession,才能开始通信。
if WCSession.default.isSupported() {
WCSession.default.delegate = self
WCSession.default.activate()
}
如果设备(如 iPhone)支持与 Apple Watch 之间进行数据通信(通过 WatchConnectivity),则返回 true,否则返回 false。
因为并非所有设备都支持 WatchConnectivity,比如没有配对 Apple Watch 的 iPhone,或者某些旧设备。通过检查 isSupported() 可以避免在不支持的设备上尝试建立连接,避免错误。
1)设置 WCSession 的代理
WCSession.default.delegate = self
设置 WCSession 的代理为当前对象(通常是视图控制器或其他类)。
例如:
WCSession.default.delegate = WCSessionDelegateImpl() // 设置 WCSession 的代理
通过设置代理,可以实现一些回调方法,处理 WatchConnectivity 会话的状态变化或接收到的数据。
常见代理方法:
session(_:activationDidCompleteWith:):会话激活完成后的回调。
session(_:didReceiveMessage:):接收到消息时的回调。
session(_:didReceiveMessageData:):接收到数据时的回调。
代理可以处理来自 Apple Watch 或 iPhone 的信息和事件。通过实现代理方法,可以在设备连接、断开或收到消息时作出响应。
2)激活 WCSession
WCSession.default.activate()
通过调用 activate() 方法,告诉系统准备好进行 WatchConnectivity 的通信。这是建立连接的关键步骤。
在激活之后,设备可以开始与配对的 Apple Watch 进行数据交换。
2、代理方法
通过代理方法,可以监听连接状态的变化,处理收到的消息,或处理发生的错误。
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
// 收到消息
}
func session(_ session: WCSession, didReceiveFile file: WCSessionFile) {
// 收到文件
}
func sessionWatchStateDidChange(_ session: WCSession) {
// 监听 Watch 状态变化(如连接、断开等)
}
3、发送消息
在应用之间发送消息时,可以使用 sendMessage 或 sendMessageData 方法。这是 WatchConnectivity 发送即时消息的核心功能。
if WCSession.default.isReachable {
WCSession.default.sendMessage(["key": "value"], replyHandler: { response in
// 处理响应
}, errorHandler: { error in
// 处理错误
})
}
isReachable 属性用于检查 iPhone 和 Apple Watch 之间是否有有效的连接,并且 Apple Watch 是否处于可以接收消息的状态。
sendMessage 用来向 Apple Watch 发送消息。消息内容是一个字典,可以在字典中放入任何需要发送的数据。
replyHandler 在 Apple Watch 成功接收到消息并作出响应时被调用。
errorHandler 用来捕获并处理发送过程中出现的任何错误,比如连接断开等问题。
常见的 WCSession 状态
isReachable:表示设备是否可达。如果 iPhone 和 Apple Watch 之间的连接正常,isReachable 将为 true。如果设备没有连接,或者 WatchOS 不可用,则为 false。
isPaired:表示设备是否已经配对。即 iPhone 是否和 Apple Watch 配对并连接。
isCompanionAppInstalled:表示 iPhone 上的应用是否已安装并可用。
WatchConnectivity 的限制
1、需要设备间连接:iPhone 和 Apple Watch 必须通过蓝牙或 Wi-Fi 连接,才能实现 WatchConnectivity 的功能。如果设备不在同一范围或没有有效连接,则无法进行数据传输。
2、限制于同一 Apple ID:确保两个设备属于同一Apple ID 下,否则无法进行通信。
3、后台限制:即使 WatchConnectivity 支持后台数据传输,但还是会有一些限制。例如,后台传输时,传输的内容可能会被延迟,或者有时需要前台应用的交互才能触发某些操作。
WatchConnectivity 触发的场景
1、应用启动时:当 iOS 或 WatchOS 应用启动时,WCSession 会尝试建立连接并进行初始化。
2、应用之间的数据交换:当用户在 iPhone 或 WatchOS 应用中发起数据交换时(例如发送消息或同步数据)。
3、设备连接或断开时:当 iPhone 与 Apple Watch 之间的连接状态发生变化时,WatchConnectivity 会触发相关代理方法,进行处理。
实际应用
在《存钱猪猪》应用中,需要将存钱罐的数据从iOS传递给WatchOS。具体来说,在iOS主应用中获取存钱罐的数据(存钱罐名称、当前金额等字段),使用WCSession将这些数据发送到Apple Watch。
1、iOS和Watch端配置WatchConnectivity 框架

在Xcode中分别找到iOS应用和Watch应用,在General中找到“Frameworks, Libraries, and Embedded Content”,点击添加按钮。
找到“WatchConnectivity.framework”框架,点击“Add”按钮进行添加。

注意:iOS端和Watch端都需要添加这一框架,Watch端还有一个WatchKit.framework,可能也需要配置上。

2、在iOS中初始化WCSession
首先,在 iOS 主应用中,需要确保 WCSession 已经被初始化并激活。
import WatchConnectivity
@main
struct pigletApp: App {
@State private var wcSessionDelegateImpl = WCSessionDelegateImpl()
init() {
if WCSession.isSupported() {
print("当前设备支持 WCSession 。")
WCSession.default.delegate = wcSessionDelegateImpl // 设置 WCSessionDelegate
WCSession.default.activate()
} else {
print("当前设备不支持 WCSession.")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.environment(wcSessionDelegateImpl)
}
}
// 定义 WCSessionDelegateImpl 来处理 WatchConnectivity 的代理方法
@Observable
class WCSessionDelegateImpl: NSObject, WCSessionDelegate {
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
print("进入 WCSessionDelegateImpl 的 session 方法")
// 处理从 WatchOS 接收到的消息
if let piggyBanks = message["piggyBanks"] as? [[String: Any]] {
print("接收到 \(piggyBanks.count) 个存钱罐数据")
for bank in piggyBanks {
if let name = bank["name"] as? String {
print("Received piggy bank: \(name)")
}
}
}
}
// 激活完成后的处理
func session(_ session: WCSession, activationDidCompleteWith state: WCSessionActivationState, error: Error?) {
if state == .activated {
print("Watch端 WCSession 激活成功")
} else {
print("Watch端 WCSession 激活失败: \(String(describing: error))")
}
}
func sessionDidBecomeInactive(_ session: WCSession) {
// 会话变为非活跃时的处理
print("WCSession 会话变为非活跃.")
}
func sessionDidDeactivate(_ session: WCSession) {
// 会话被停用时的处理
print("WCSession 会话被停用.")
}
}
WCSessionDelegateImpl 类:
这个类是 WCSessionDelegate 协议的实现,用来处理与 WatchOS 的通信。
需要实现的主要方法包括:
1)session(_:didReceiveMessage:):当接收到来自 WatchOS 的消息时,会被调用。
2)session(_:activationDidCompleteWith:error:):会话激活完成后的回调。
3)sessionDidBecomeInactive(_:):当会话进入非活跃状态时被调用。
4)sessionDidDeactivate(_:):当会话被停用时被调用。
WCSessionDelegateImpl也可以单独放在一个Swift文件中进行管理。
3、获取数据并发送到 WatchOS
在主视图中,获取存钱罐数据并将其发送到 WatchOS。可以在视图加载时或者通过某个操作(如按钮点击)来触发数据发送。
import SwiftUI
import WatchConnectivity
struct Home: View {
@Query var allPiggyBank: [PiggyBank]
var body: some View {
VStack {
// 主视图内容,显示存钱罐列表等
Button("Send Data to Watch") {
sendPiggyBankDataToWatch()
}
}
}
// 将 PiggyBank 数据发送到 WatchOS
func sendPiggyBankDataToWatch() {
print("进入:sendPiggyBankDataToWatch")
// 确保 WCSession 激活
if WCSession.default.isReachable {
print("iOS与Watch链接成功")
// 创建存钱罐数据字典
let piggyBankData: [[String: Any]] = allPiggyBank.map { bank in
return [
"name": bank.name,
"icon": bank.icon,
"amount": bank.amount,
"targetAmount": bank.targetAmount,
"isPrimary": bank.isPrimary
]
}
print("piggyBankData:\(piggyBankData)")
print("发送数据到 WatchOS")
// 将数组包装在字典中
let wcSessionPiggyBanks: [String: Any] = ["piggyBanks": piggyBankData]
WCSession.default.transferUserInfo(wcSessionPiggyBanks)
print("Data sent: \(wcSessionPiggyBanks)")
} else {
print("iOS与Watch未连接,isReachable = false")
}
}
}
isReachable只有在iOS主应用和Watch应用同时打开并亮屏的情况下,才为true,否则为false。
在主视图中点击Button后,会将SwiftData中allPiggyBank数组(即所有存钱罐的数据)映射成多个字典,每个字典代表一个存钱罐的数据。每个字典包含以下键值对:
“name”: 存钱罐名称
“icon”: 存钱罐图标名称
“amount”: 当前金额
“targetAmount”: 目标金额
“isPrimary”: 是否为主要存钱罐
返回的结果:
原始数组 piggyBank 是 [PiggyBank] 类型,即多个 PiggyBank 对象的数组。
每个 PiggyBank 对象会被转换成字典 [String: Any] 类型。
最终 piggyBankData 的类型是 [[String: Any]],即包含多个字典的数组。
transferUserInfo方法用于传输较小的键值对数据(通常是字典格式)。它将包含存钱罐数据的字典发送到 WatchOS。
需要注意的是,transferUserInfo期望传入的是一个字典类型 ( [ String: Any ] ),因此需要将piggyBankData 从数组转换为字典。
let wcSessionPiggyBanks: [String: Any] = ["piggyBanks": piggyBankData]
WCSession.default.transferUserInfo(wcSessionPiggyBanks)
4、在 WatchOS 中接收数据
在 WatchOS 中,确保在 Watch 应用中实现 WCSessionDelegate,并接收来自 iOS 主应用的数据。
import WatchConnectivity
@main
struct BankletWatch_Watch_AppApp: App {
@State private var wcSessionDelegateImpl = WatchSessionDelegate()
init() {
// 激活 WCSession
if WCSession.isSupported() {
print("激活 WCSession")
WCSession.default.delegate = wcSessionDelegateImpl // 设置 WCSession 的代理
WCSession.default.activate() // 激活 WCSession
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.environment(wcSessionDelegateImpl)
}
}
// 定义 WatchSessionDelegate 来接收 iOS主应用传递的数据
@Observable
class WatchSessionDelegate: NSObject, WCSessionDelegate {
// 存储接收到的数据
var piggyBanks: [PiggyBank] = []
// 处理从 iOS 应用发送过来的消息
func session(_ session: WCSession, didReceiveUserInfo wcSessionPiggyBanks: [String : Any]) {
// 接收到的 userInfo 字典
print("Received user info: \(wcSessionPiggyBanks)")
if let piggyBank = wcSessionPiggyBanks["piggyBanks"] as? [[String: Any]] {
print("piggyBank:\(piggyBank)")
piggyBanks = piggyBank.compactMap {
if let name = $0["name"] as? String,
let icon = $0["icon"] as? String,
let amount = $0["amount"] as? Double,
let targetAmount = $0["targetAmount"] as? Double,
let isPrimary = $0["isPrimary"] as? Bool {
return PiggyBank(name: name, icon: icon, amount: amount, targetAmount: targetAmount, isPrimary: isPrimary)
}
return nil // 如果解包失败,则返回 nil
}
}
print("piggyBanks:\(piggyBanks)")
}
// 激活完成后的处理
func session(_ session: WCSession, activationDidCompleteWith state: WCSessionActivationState, error: Error?) {
if let error = error {
print("Watch端 WCSession 激活失败: \(error)")
} else {
print("Watch端 WCSession 激活成功,状态: \(state.rawValue)") // 输出状态
}
}
}
struct PiggyBank: Codable {
var name: String
var icon: String
var amount: Double
var targetAmount: Double
var isPrimary: Bool
}
BankletWatch_Watch_AppApp
WatchOS 应用的入口点(@main),它负责设置并启动应用。BankletWatch_Watch_AppApp 初始化时会执行以下步骤:
WCSession.default.activate():激活 WCSession,这是与 iOS 设备进行数据传输的必需步骤。通过激活 WCSession,WatchOS 可以与 iOS 应用建立连接并进行通信。
WCSession.default.delegate = wcSessionDelegateImpl:设置 WatchOS 的 WCSessionDelegate 为 wcSessionDelegateImpl,这个代理类负责处理来自 iOS 端的消息。
@State private var wcSessionDelegateImpl = WatchSessionDelegate():声明一个 wcSessionDelegateImpl 变量,用来管理 WatchOS 与 iOS 之间的连接。
WatchSessionDelegate
这是 WatchOS 应用的代理类,负责处理 WCSession 相关的回调。它实现了 WCSessionDelegate 协议,能够处理数据接收、激活和错误等事件。
session(_:didReceiveUserInfo:)
该方法是用来接收从 iOS 应用发送的用户信息数据。在这里,它将从 iOS 传递过来的 piggyBanks 数据解码,并存储到 piggyBanks 数组中。解码使用 compactMap 来确保只有成功解包的 PiggyBank 对象会被存储。
session(_:activationDidCompleteWith:)
当 WCSession 激活完成时,这个回调方法会被调用。在这个方法里,可以检查 WCSession 是否成功激活,并处理可能的错误。
PiggyBank 数据模型
PiggyBank 是用来表示存钱罐的数据模型。它符合 Codable 协议,意味着它可以被序列化为 JSON 格式,并且可以从 JSON 格式反序列化。模型包含以下属性:
name: 存钱罐的名称
icon: 存钱罐的图标
amount: 当前存款金额
targetAmount: 目标存款金额
isPrimary: 是否为主要存钱罐
5、在WatchOS显示内容
struct Home: View {
@Environment(WatchSessionDelegate.self) var wcSessionDelegateImpl // 从环境中读取 Person
var body: some View {
VStack {
if !wcSessionDelegateImpl.piggyBanks.isEmpty {
Text("\(mainPiggyBank.name)")
.font(.footnote)
} else {
Image("emptyBox")
.resizable()
.scaledToFit()
.frame(width: 100)
}
}
}
}
从Enviroment中通过wcSessionDelegateImpl接收数据,在Text视图中将相关内容显示出来。
总结
WatchConnectivity 是 iOS 和 WatchOS 之间的数据传输桥梁,通过它,可以在这两种设备上实现信息同步、即时消息传递以及背景数据传输等功能。通过正确实现 WCSession,使得 iPhone 和 Apple Watch 应用之间的互动更为流畅和高效。
需要注意的是,WCSession.default.isReachable只有在iOS端和Watch端都打开的情况下,才可以实现数据的传递,因此并不是适用于只打开Watch端的情况下显示数据。
从WCSession中传递过来的数据仍然需要存储,因此如果是少量数据,还是建议使用App Group传递。
最后是在探索的过程中发现,AppWatch在接收iOS应用传递时,存在一个初始化的过程。iOS向Watch传递数据时,第一次很有可能传递失败,或者Watch在首次接收数据时会进行初始化,等待几秒后的第二次才能成功,因此在实际应用中还需要考虑首次初始化的情况,这一问题具体原因有待研究。
扩展知识
Xcode提示Connecting to 方君宇的Apple Watch
如果在连接Watch的过程中,存在提示:
Connecting to 方君宇的Apple Watch
可以参考《Xcode提示Connecting to 方君宇的Apple Watch》。
后台接受远程通知
如果Watch需要执行额外的任务,比如定期在后台获取IOS数据,更新应用内容,可以在Capabilities 中启用 Remote Notifications功能。
在 Xcode 中,确保 iOS Capabilities 配置正确。
打开 iOS 应用的 Target,进入 Signing & Capabilities 选项卡,确保以下内容:

Background Modes:启用 Background fetch 和 Remote notifications(如果需要后台通信)。
Push Notifications:如果需要推送通知,确保启用。


对于 WatchOS 应用,通常不需要额外的 Capabilities 配置。
具体的后台同步内容则需要根据自身的需求进行设计。
相关文章
Xcode提示Connecting to 方君宇的Apple Watch:https://fangjunyu.com/2025/02/19/xcode%e6%8f%90%e7%a4%baconnecting-to-%e6%96%b9%e5%90%9b%e5%ae%87%e7%9a%84apple-watch/