WatchConnectivity:WCSession.transferUserInfo 在手表上成功,但数据从未到达 iPhone

如何解决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/Applicationupport/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-66B8B41C/ -- 目的 ID:2F068AB7-4DC3-4A75-9745-A74AD9179CA4 -- 声明 ID:FF99C583-0CED-44D0-8403-BD342FCE6CAC

流程:WCExperimentsWatchApp 扩展子系统: com.apple.foundation.filecoordination 类别:索赔消息:阅读 选项: 1 -- URL: Library/Applicationupport/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-66B8B41C/ -- 目的 ID:D53FBF3C-8B2C-48FB-9390-9058CAD4C757 -- claimID:62292E97-3529-415A-B6B3-1768387D67B4

在 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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-