WatchConnectivity:WCSession.transferUserInfo 在手表上成功,但数据从未达到 iPhone
WatchConnectivity: WCSession.transferUserInfo succeeds on a watch but the data never reaches an iPhone
上下文
我正在使用 Xcode 12.3.
为 iOS 应用程序(即它不是独立的 watchOS 应用程序)构建一个 watchOS 应用程序
我的 watchOS 应用程序需要根据手表上的用户操作向 iOS 发送少量数据(包含字符串和日期的结构),然后 iOS 应用程序将将接收到的数据保存在自己的存储中。来自手表的数据不需要立即传输,但也不能丢失,最终必须到达 iOS 应用程序。我认为 WCSession
的 transferUserInfo(_:)
方法最适合我的需要。
问题
当我在 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, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
logger.notice("WCSession activationDidCompleteWith state: \(activationState), 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, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?)
被调用并且它没有报告任何错误。例如:"WCSession didFinish userInfoTransfer: ["number": 683], error: nil".
之后,我在连接手表模拟器的Console.app中看到如下日志:
Process: wcd (Foundation) Subsystem:
com.apple.foundation.filecoordination Category: claims Message: Write
options: 8 -- URL: Library/Application<decode: mismatch for [%20S] got
[SCALAR
sz:8]>upport/com.apple.watchconnectivity/E88632F5-57F5-49F9-9CE8-98C1AA6C31CB/UserInfoTransfers/contents.index
-- file:///Users/myusername/Library/Developer/CoreSimulator/Devices/753CCF4F-FFAD-4416-BBC6-E23234D0A500/data/Containers/Data/Application/4802344B-0A89-493C-82B3-60D41C76B8B2/
-- purposeID: 2F068AB7-4DC3-4A75-9745-A74AD9179CA4 -- claimID: FF99C583-0CED-44D0-8403-BD342FCE6CAC
Process: WCExperimentsWatchApp Extension Subsystem:
com.apple.foundation.filecoordination Category: claims Message: Read
options: 1 -- URL: Library/Application<decode: mismatch for [%20S] got
[SCALAR
sz:8]>upport/com.apple.watchconnectivity/E88632F5-57F5-49F9-9CE8-98C1AA6C31CB/UserInfoTransfers/contents.index
-- file:///Users/myusername/Library/Developer/CoreSimulator/Devices/753CCF4F-FFAD-4416-BBC6-E23234D0A500/data/Containers/Data/Application/4802344B-0A89-493C-82B3-60D41C76B8B2/
-- purposeID: D53FBF3C-8B2C-48FB-9390-9058CAD4C757 -- claimID: 62292E97-3529-415A-B6B3-1768387D67B4
在 iOS 方面,WCSessionDelegate 方法 session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:])
永远不会被调用,并且 iOS 应用程序永远不会从手表接收任何数据。
我尝试了不同的模拟器,尝试重置模拟器的所有内容和设置,但没有效果。不幸的是,我无法使用真正的 Apple Watch 对其进行测试,因为我没有。
有人可以建议我是否做错了什么吗?如何正确地将 userInfo 从手表发送到 iPhone?
提前致谢。
我重现了您的示例项目,发现它产生了与您遇到的相同的故障。我还发现将通信方法更改为 sending/receiving 一条消息而不是 userInfo(两者都是字典)解决了模拟器中的问题。
我对您的示例所做的更改..
在 phone 应用中:
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)")
}
}
Apple's docs 最后包括以下警告:
Warning
Always test Watch Connectivity data transfers on paired
devices. The Simulator app doesn’t support the transferUserInfo(_:)
method.
transferFile(_:metadata:)
也是如此。
上下文
我正在使用 Xcode 12.3.
为 iOS 应用程序(即它不是独立的 watchOS 应用程序)构建一个 watchOS 应用程序我的 watchOS 应用程序需要根据手表上的用户操作向 iOS 发送少量数据(包含字符串和日期的结构),然后 iOS 应用程序将将接收到的数据保存在自己的存储中。来自手表的数据不需要立即传输,但也不能丢失,最终必须到达 iOS 应用程序。我认为 WCSession
的 transferUserInfo(_:)
方法最适合我的需要。
问题
当我在 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, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
logger.notice("WCSession activationDidCompleteWith state: \(activationState), 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, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?)
被调用并且它没有报告任何错误。例如:"WCSession didFinish userInfoTransfer: ["number": 683], error: nil".
之后,我在连接手表模拟器的Console.app中看到如下日志:
Process: wcd (Foundation) Subsystem: com.apple.foundation.filecoordination Category: claims Message: Write options: 8 -- URL: Library/Application<decode: mismatch for [%20S] got [SCALAR sz:8]>upport/com.apple.watchconnectivity/E88632F5-57F5-49F9-9CE8-98C1AA6C31CB/UserInfoTransfers/contents.index -- file:///Users/myusername/Library/Developer/CoreSimulator/Devices/753CCF4F-FFAD-4416-BBC6-E23234D0A500/data/Containers/Data/Application/4802344B-0A89-493C-82B3-60D41C76B8B2/ -- purposeID: 2F068AB7-4DC3-4A75-9745-A74AD9179CA4 -- claimID: FF99C583-0CED-44D0-8403-BD342FCE6CAC
Process: WCExperimentsWatchApp Extension Subsystem: com.apple.foundation.filecoordination Category: claims Message: Read options: 1 -- URL: Library/Application<decode: mismatch for [%20S] got [SCALAR sz:8]>upport/com.apple.watchconnectivity/E88632F5-57F5-49F9-9CE8-98C1AA6C31CB/UserInfoTransfers/contents.index -- file:///Users/myusername/Library/Developer/CoreSimulator/Devices/753CCF4F-FFAD-4416-BBC6-E23234D0A500/data/Containers/Data/Application/4802344B-0A89-493C-82B3-60D41C76B8B2/ -- purposeID: D53FBF3C-8B2C-48FB-9390-9058CAD4C757 -- claimID: 62292E97-3529-415A-B6B3-1768387D67B4
在 iOS 方面,WCSessionDelegate 方法 session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:])
永远不会被调用,并且 iOS 应用程序永远不会从手表接收任何数据。
我尝试了不同的模拟器,尝试重置模拟器的所有内容和设置,但没有效果。不幸的是,我无法使用真正的 Apple Watch 对其进行测试,因为我没有。
有人可以建议我是否做错了什么吗?如何正确地将 userInfo 从手表发送到 iPhone?
提前致谢。
我重现了您的示例项目,发现它产生了与您遇到的相同的故障。我还发现将通信方法更改为 sending/receiving 一条消息而不是 userInfo(两者都是字典)解决了模拟器中的问题。
我对您的示例所做的更改..
在 phone 应用中:
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)")
}
}
Apple's docs 最后包括以下警告:
Warning
Always test Watch Connectivity data transfers on paired devices. The Simulator app doesn’t support the transferUserInfo(_:) method.
transferFile(_:metadata:)
也是如此。