关于WCSession transferUserInfo的问题:Apple Watch发送CLLocation至iPhone无响应
解决Apple Watch通过WCSession transferUserInfo发送CLLocation无响应的问题
嘿,我之前也踩过这个一模一样的坑,咱们一步步排查为什么你的接收方法没触发:
1. 先确认WCSession的激活和配置没毛病
你代码里提到了检查WCSession.default.activationState,但要确保这几点:
- 手表和iPhone两端都得在生命周期早期就初始化并激活WCSession——比如iPhone端在
application(_:didFinishLaunchingWithOptions:)里,手表端在awake(withContext:)或者willActivate()里,不能等位置更新了才去激活,那时候可能已经晚了。 - 两端都要设置
delegate,并且必须实现session(_:activationDidCompleteWith:error:)这个代理方法,用来确认激活是否成功——要是激活失败了,后面的传输肯定没戏。 - 发送前一定要检查
WCSession.default.isReachable是否为true——如果设备没连接,transferUserInfo会把数据缓存起来,等恢复连接才发,测试时你可能误以为没触发,其实是在排队呢。
2. CLLocation的序列化是重灾区
transferUserInfo要求字典里的值必须是plist兼容类型,或者遵循NSCoding的对象。虽然CLLocation确实遵循NSCoding,但直接塞到userInfo里有时候会出问题,建议手动归档后再发:
// 手表端发送代码修改 if let location = locations.first, WCSession.default.isReachable { do { let locationData = try NSKeyedArchiver.archivedData(withRootObject: location, requiringSecureCoding: false) let userInfo = ["locationData": locationData] WCSession.default.transferUserInfo(userInfo) print("位置数据已发送") } catch { print("序列化CLLocation失败:\(error.localizedDescription)") } }
然后iPhone端接收时再反序列化:
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { DispatchQueue.main.async { if let data = userInfo["locationData"] as? Data, let location = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? CLLocation { // 这里处理你的CLLocation对象 print("收到位置:\(location.coordinate.latitude), \(location.coordinate.longitude)") } } }
直接传CLLocation的话,系统有时候会因为序列化不彻底导致传输失败,接收端自然不会触发方法。
3. 接收端的代理方法要搞对
别搞混了方法!用transferUserInfo发送的话,对应的接收方法是session(_:didReceiveUserInfo:),不是session(_:didReceiveMessage:)(那个是即时消息用的)。另外要确保iPhone端的WCSessionDelegate确实实现了这个方法,而且没有拼写错误。
4. 测试场景的小细节
- 测试时确保手表和iPhone是配对且正常连接的(蓝牙或Wi-Fi都可以,别开飞行模式)。
- 如果用模拟器,要确认Watch模拟器和iPhone模拟器是配对状态(在Xcode的「Window」→「Devices and Simulators」里能设置)。
- 有时候Xcode的调试缓存会抽风,试试重启Xcode、模拟器/真机,重新安装App,说不定就好了。
最后给你一个完整的示例参考:
手表端完整发送代码
class InterfaceController: WKInterfaceController, WCSessionDelegate, CLLocationManagerDelegate { var locationManager: CLLocationManager! override func awake(withContext context: Any?) { super.awake(withContext: context) // 初始化并激活WCSession if WCSession.isSupported() { WCSession.default.delegate = self WCSession.default.activate() } // 初始化位置管理器 locationManager = CLLocationManager() locationManager.delegate = self locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let location = locations.first, WCSession.default.isReachable else { return } do { let locationData = try NSKeyedArchiver.archivedData(withRootObject: location, requiringSecureCoding: false) WCSession.default.transferUserInfo(["location": locationData]) } catch { print("序列化失败:\(error)") } } // 必须实现的WCSession代理方法 func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { if let error = error { print("WCSession激活失败:\(error)") } } func sessionDidBecomeInactive(_ session: WCSession) {} func sessionDidDeactivate(_ session: WCSession) {} }
iPhone端完整接收代码
class ViewController: UIViewController, WCSessionDelegate { override func viewDidLoad() { super.viewDidLoad() if WCSession.isSupported() { WCSession.default.delegate = self WCSession.default.activate() } } func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { DispatchQueue.main.async { if let data = userInfo["location"] as? Data, let location = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? CLLocation { // 处理位置数据,比如更新UI print("收到位置:\(location.coordinate)") } } } func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {} func sessionDidBecomeInactive(_ session: WCSession) {} func sessionDidDeactivate(_ session: WCSession) {} }
按照这个思路排查,大概率是序列化或者WCSession激活的问题,试试看!
内容的提问来源于stack exchange,提问作者Evel Knievel
相关产品推荐
相关产品推荐

