iOS与watchOS配套应用中transferUserInfo手表到手机传输失效,提示代理为空但已实现对应方法
我之前调试WatchConnectivity时也踩过几乎一模一样的坑,结合你的报错信息和代码片段,咱们一步步拆解可能的问题:
1. 最核心的问题:你没实现transferUserInfo对应的代理方法
你的报错明确指出session:didReceiveUserInfo:未实现,但从你贴的代码看,你只写了didReceiveMessage(对应**实时消息sendMessage的回调),完全没实现后台传输transferUserInfo**的接收方法!
划重点:sendMessage和transferUserInfo是两套独立的API,对应完全不同的代理回调:
- 实时消息
sendMessage→ 触发session(_:didReceiveMessage:) - 后台传输
transferUserInfo→ 触发session(_:didReceiveUserInfo:)或带完成回调的session(_:didReceiveUserInfo:fetchCompletionHandler:)
你需要在WatchConnectivityManager里补充这两个方法(推荐实现带完成回调的版本,符合iOS 13+的规范):
// 处理transferUserInfo的基础回调 func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any]) { received(userInfo) } // 处理transferUserInfo的带完成回调版本(推荐) func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any], fetchCompletionHandler completionHandler: @escaping (WCSessionUserInfoFetchResult) -> Void) { received(userInfo) completionHandler(.newData) // 告诉系统已完成消息处理 }
2. 确认iOS端WatchConnectivityManager的生命周期稳定
报错里的delegate (null)是关键信号——这意味着你的WatchConnectivityManager实例被销毁了,WCSession找不到接收回调的代理对象。
你必须确保iOS端的这个管理器是全局持久化持有的:
- 最稳妥的方式是用单例模式:
class WatchConnectivityManager: NSObject, WCSessionDelegate, ObservableObject { // 全局单例,确保实例不会被销毁 static let shared = WatchConnectivityManager() private override init() { super.init() setupWCSession() } private func setupWCSession() { guard WCSession.isSupported() else { return } // 先设置代理,再激活session!顺序不能反 WCSession.default.delegate = self WCSession.default.activate() } // 你的原有代码... } - 然后在iOS App启动时就初始化并持有它:
绝对不能用@main struct YouriOSApp: App { // 用@StateObject确保实例在App生命周期内不会被销毁 @StateObject private var connectivityManager = WatchConnectivityManager.shared var body: some Scene { WindowGroup { ContentView() .environmentObject(connectivityManager) } } }@State或局部变量持有这个实例,否则SwiftUI会随时销毁它。
3. 检查WCSession的代理设置与激活顺序
必须严格遵循先设置代理,再激活session的顺序,如果反过来操作,即使后续设置了代理,系统也无法正确关联回调逻辑。
如果你的WatchConnectivityManager里没有初始化方法,一定要补上像上面单例代码里的setupWCSession逻辑,确保代理绑定在激活之前完成。
4. 补充:后台状态下的传输注意事项
当iOS App被完全杀死时,transferUserInfo的消息会在App下次启动时触发回调,这时候必须确保你的WatchConnectivityManager在App启动的第一时间就初始化并激活session,否则会错过这次回调。
先补全didReceiveUserInfo方法,再确认管理器的生命周期,应该就能解决这个“代理为空”的诡异问题了!
内容来源于stack exchange

