如何解决WatchConnectivity:WCSession.transferUserInfo 在手表上成功,但数据从未到达 iPhone
上下文
我正在使用 Xcode 12.3 为 iOS 应用程序构建 watchOS 应用程序(即它不是独立的 watchOS 应用程序)。
由于用户在手表上的操作,我的 watchOS 应用程序需要向 iOS 发送少量数据(包含字符串和日期的结构),然后 iOS 应用程序会将接收到的数据保存在自己的存储中。手表中的数据不需要立即传输,但也不能丢失,最终必须到达 iOS 应用程序。我认为 transferUserInfo(_:)
的 WCSession
方法最适合我的需要。
问题
当我在 watchOS 模拟器的默认 WCSession 上调用 transferUserInfo_:
时,一切都成功了,但数据永远不会到达对应的 iOS 应用。
我使用 Xcode 12.3、iOS 14.3 模拟器和配对的 watchOS 7.2 模拟器。
我确保在应用启动时激活 WCSession 并检查它是否已成功激活,并在尝试传输用户信息之前检查会话是否处于活动状态。
奇怪的是,我可以使用 WCSession 的 updateApplicationContext(_:)
方法将数据从 iOS 传输到 watchOS,但无法将数据从手表传输回 iOS 应用。
代码
我已经构建了一个说明问题的最小示例。 watchOS 应用程序显示一个按钮,当用户点击它时,它应该向 iOS 发送一个随机数。 iOS 应用程序应该在屏幕上的文本标签中显示收到的号码。
这是示例 iOS 应用的完整代码,它只是一个文件:
import os
import SwiftUI
import WatchConnectivity
class WatchConnectivityService: NSObject,ObservableObject {
@Published private(set) var receivedNumber: Int
private let logger = Logger(subsystem: "WCExperimentsiOSApp",category: "WatchConnectivityService")
private let wcSession: WCSession
override init() {
self.receivedNumber = -9999
self.wcSession = WCSession.default
super.init()
if WCSession.isSupported() {
wcSession.delegate = self
wcSession.activate()
}
}
}
extension WatchConnectivityService: WCSessionDelegate {
func session(_ session: WCSession,activationDidCompleteWith activationState: WCSessionActivationState,error: Error?) {
logger.notice("WCSession activationDidCompleteWith state: \(activationState),error: \(error?.localizedDescription ?? "nil")")
}
func sessionDidBecomeInactive(_ session: WCSession) {
logger.notice("WCSession sessionDidBecomeInactive")
}
func sessionDidDeactivate(_ session: WCSession) {
logger.notice("WCSession sessionDidDeactivate")
}
func session(_ session: WCSession,didReceiveUserInfo userInfo: [String : Any] = [:]) {
logger.notice("Received userInfo: \(userInfo)")
if let number = userInfo["number"] as? Int {
DispatchQueue.main.async {
self.receivedNumber = number
}
}
}
}
@main
struct WatchConnectivityExperimentsApp: App {
@StateObject var service = WatchConnectivityService()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(service)
}
}
}
struct ContentView: View {
@EnvironmentObject var service: WatchConnectivityService
var body: some View {
Text("Received number: \(service.receivedNumber)")
}
}
一旦 iOS 应用程序启动,我可以看到一条关于成功激活 WCSession 的日志消息,它看起来像这样:“WCSession activationDidCompleteWith state:activated,error: nil”。
这是 watchOS 扩展的完整代码:
import os
import SwiftUI
import WatchConnectivity
class WatchConnectivityService: NSObject,ObservableObject {
private let wcSession: WCSession
private let logger = Logger(subsystem: "WCExperimentsWatchApp",category: "WatchConnectivityService")
override init() {
self.wcSession = WCSession.default
super.init()
if WCSession.isSupported() {
wcSession.delegate = self
wcSession.activate()
}
}
func sendNumberToiOS() {
guard wcSession.activationState == .activated else {
logger.error("Error attempting to transfer user info: WCSession is not activated")
return
}
let randomNumber = Int.random(in: 1...1000)
wcSession.transferUserInfo(["number": randomNumber])
}
}
extension WatchConnectivityService: WCSessionDelegate {
func session(_ session: WCSession,error: \(error?.localizedDescription ?? "nil")")
}
func session(_ session: WCSession,didFinish userInfoTransfer: WCSessionUserInfoTransfer,error: Error?) {
logger.notice("WCSession didFinish userInfoTransfer: \(userInfoTransfer.userInfo),error: \(error?.localizedDescription ?? "nil")")
}
}
@main
struct WatchConnectivityExperimentsApp: App {
@StateObject var service = WatchConnectivityService()
var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
.environmentObject(service)
}
}
}
}
struct ContentView: View {
@EnvironmentObject var service: WatchConnectivityService
var body: some View {
VStack {
Button(action: { service.sendNumberToiOS() },label: {
Text("Send random number to iOS app")
})
}
}
}
在 watchOS 应用程序中,我还看到一条日志消息,表明 WCSession 在启动时已成功激活。
当我点击手表模拟器上的按钮时,我看到 WCSessionDelegate 方法 func session(_ session: WCSession,error: Error?)
被调用并且它没有报告错误。例如:"WCSession didFinish userInfoTransfer: ["number": 683],error: nil"。
之后,我在连接到手表模拟器的Console.app中看到如下日志:
进程:wcd(基金会)子系统:
com.apple.foundation.filecoordination 类别:索赔 消息:写
options: 8 -- URL: Library/Application
流程:WCExperimentsWatchApp 扩展子系统:
com.apple.foundation.filecoordination 类别:索赔消息:阅读
选项: 1 -- URL: Library/Application
在 iOS 端,WCSessionDelegate 方法 session(_ session: WCSession,didReceiveUserInfo userInfo: [String : Any] = [:])
永远不会被调用,iOS 应用程序也永远不会从手表接收任何数据。
我尝试了不同的模拟器,尝试重置模拟器的所有内容和设置,但没有效果。不幸的是,我无法使用真正的 Apple Watch 对其进行测试,因为我没有。
有人可以建议我是否做错了什么吗?如何正确地将用户信息从手表发送到 iPhone?
提前致谢。
解决方法
我复制了您的示例项目,发现它产生了与您遇到的相同的失败。我还发现将通信方法更改为发送/接收消息而不是 userInfo(两者都是字典)解决了模拟器中的问题。
我对您的示例所做的更改..
在手机应用中:
func session(_ session: WCSession,didReceiveMessage message: [String : Any]) {
logger.notice("Received userInfo: \(message)")
if let number = message["number"] as? Int {
DispatchQueue.main.async {
self.receivedNumber = number
}
}
}
在手表应用中:
func sendNumberToiOS() {
logger.notice("WCSession isReachable: \(self.wcSession.isReachable)")
guard wcSession.activationState == .activated else {
logger.error("Error attempting to transfer user info: WCSession is not activated")
return
}
let randomNumber = Int.random(in: 1...1000)
wcSession.sendMessage(["number": randomNumber],replyHandler: nil) { (error) in
self.logger.error("Error sending message: \(error.localizedDescription)")
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。