WCSession
我有一个 Watch OS 2 应用程序,它通过方法与 iOS 应用程序通信sendMessage:replyHandler:errorHandler:
iOS 应用程序正确回复,但有时我会收到7014
域代码错误WCErrorDomain
:“无法交付有效负载”
当 iOS 应用程序不在前台时,它会更频繁地发生。
我没有找到这个问题的任何解决方案,我希望你们中的一个知道这个问题的解决方案
对于在 iOS10 beta 6 和 GM 上遇到问题并且您使用的是 Swift3 的任何人,解决方案是将 iOS 应用程序中的委托函数标头更改为以下内容:
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
注意 @escaping 和 Any 而不是 AnyObject 类型。
就我而言,我必须实现两个代表:
一个没有任何replyHandler
func session(_ session: WCSession,
didReceiveMessage message: [String : Any])
与_ replyHandler
func session(_ session: WCSession,
didReceiveMessage message: [String : Any],
replyHandler: @escaping ([String : Any]) -> Void)
如果您发送没有 a 的消息,replyHandler
则第一个委托运行。
如果您发送带有 a 的消息,replyHandler
则第二个代表运行。
在某些情况下,我只发送一条消息,而在其他情况下,我正在发送一条消息并期待对方的回复。
但是...我只实现了第二个代表-_-
无论如何,最终为了减少重复代码,我实现了一个通用方法并最终得到:
func session(_ session: WCSession,
didReceiveMessage message: [String : Any]) {
handleSession(session,
didReceiveMessage: message)
}
func session(_ session: WCSession,
didReceiveMessage message: [String : Any],
replyHandler: @escaping ([String : Any]) -> Void) {
handleSession(session,
didReceiveMessage: message,
replyHandler: replyHandler)
}
//Helper Method
func handleSession(_ session: WCSession,
didReceiveMessage message: [String : Any],
replyHandler: (([String : Any]) -> Void)? = nil) {
//Common logic
}
观看操作系统 4
试试这个,这解决了我的问题。在 InterfaceController 中添加以下方法用于将数据传递给手机。
-(void)sendDataToPhone:(NSDictionary* _Nonnull)dictData
{
if(WCSession.isSupported){
WCSession* session = WCSession.defaultSession;
session.delegate = self;
[session activateSession];
if(session.reachable)
{
[session sendMessage:dictData replyHandler: ^(NSDictionary<NSString *,id> * __nonnull replyMessage) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@".....replyHandler called --- %@",replyMessage);
// Play a sound in watch
[[WKInterfaceDevice currentDevice] playHaptic:WKHapticTypeSuccess];
});
}
errorHandler:^(NSError * __nonnull error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Error = %@",error.localizedDescription);
});
}
];
}
else
NSLog(@"Session Not reachable");
}
else
NSLog(@"Session Not Supported");
}
#pragma mark - Standard WatchKit delegate
-(void)sessionWatchStateDidChange:(nonnull WCSession *)session
{
if(WCSession.isSupported){
WCSession* session = WCSession.defaultSession;
session.delegate = self;
[session activateSession];
}
}
在手机端,添加以下代码,用于接收手表的数据。
在 didFinishLaunchingWithOptions 中添加以下内容。
// Allocating WCSession inorder to communicate back to watch.
if(WCSession.isSupported){
WCSession* session = WCSession.defaultSession;
session.delegate = self;
[session activateSession];
}
现在添加 WCSessionDelegate。
#pragma mark - WCSession Delegate
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *, id> *)message replyHandler:(void(^)(NSDictionary<NSString *, id> *replyMessage))replyHandler
{
if(message){
NSData *receivedData = [message objectForKey:@"AudioData"];
NSDictionary* response = @{@"response" : [NSString stringWithFormat:@"Data length: %lu",(unsigned long)receivedData.length]} ;
replyHandler(response);
}
}
#pragma mark - Standard WatchKit Delegate
-(void)sessionWatchStateDidChange:(nonnull WCSession *)session
{
if(WCSession.isSupported){
WCSession* session = WCSession.defaultSession;
session.delegate = self;
[session activateSession];
if(session.reachable){
NSLog(@"session.reachable");
}
if(session.paired){
if(session.isWatchAppInstalled){
if(session.watchDirectoryURL != nil){
}
}
}
}
}
希望这可以帮助你:)
抱歉,我没有足够的声誉来评论答案。Peter Robert 的回答解决了我的问题:出现了 Swift 3 WCErrorCodeDeliveryFailed ,解决方案只是在 replyHandlers 上将 AnyObject 更改为 Any。
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
//code
replyHandler (answer as [String : Any])
}
我遇到了同样的问题,并且在应用程序生命周期的后期移动 WCSession 初始化(设置委托并激活它)解决了这个问题。
我在应用程序代表 didFinishLaunching 中激活了 WCSession,并且在那里中断了通信。稍后在应用程序中移动 WCSession 初始化使通信再次工作。
就我而言,我将 WCSessionDelegate(iOS 端) 放在一个单独的类中并将其初始化为局部变量。将其更改为全局实例变量解决了这个问题。
所以我的 iOS 代码是:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
SessionHandler()
}
更改为以下以使其正常工作:
var handler: SessionHandler!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
handler = SessionHandler()
}
确保您的会话始终处于活动状态。例如,我有另一个视图,它是测试的一部分,然后在最初的视图上返回,我想知道为什么会话不再处于活动状态。
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
[super willActivate];
//Setup WCSession
if ([WCSession isSupported]) {
[[WCSession defaultSession] setDelegate:self];
[[WCSession defaultSession] activateSession];
}}
上面为我做了。如果它首先放在 awakeWithContext 中,愚蠢的我....
这个场景将涵盖几个用例。请看一下这些步骤,它对我有很大帮助。
1 - 了解每个设备必须配置自己的 WCSession 实例并配置适当的委托。
2 - 仅在每个设备上的一个地方实现 WCSessionDelegate ,ej。在 AppDelegate 上的 iOS 应用程序上,在 ExtensionDelegate 上的 watchOS 上。这非常重要,因为在 watchOS 上配置了适当的 WCSession,但在 iPhone 上实现了两个不同的地方,ej。在应用程序委托上,然后在应用程序的第一个 viewcontorllweer 上,(在我的情况下)导致不稳定的行为,这就是有时 iOS 应用程序在从手表收到消息时停止响应的主要原因。
3 - 建议仅在主机应用程序上重新激活会话。这是我的 iOS 应用程序的一个示例,它只有一个 WCSessionDelegate。(应用委托)
#pragma mark - WCSessionDelegate
- (void)session:(WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(NSError *)error{
if( activationState == WCSessionActivationStateActivated) {
NSLog(@"iPhone WKit session Activated");
}else if (activationState == WCSessionActivationStateInactive) {
NSLog(@"iPhone WKit Inactive");
}else if (activationState == WCSessionActivationStateNotActivated) {
NSLog(@"iPhone WKit NotActivated");
}
}
- (void)sessionDidBecomeInactive:(WCSession *)session{
/*
The session calls this method when it detects that the user has switched to a different Apple Watch. While in the inactive state, the session delivers any pending data to your delegate object and prevents you from initiating any new data transfers. After the last transfer finishes, the session moves to the deactivated state
*/
NSLog(@"sessionDidBecomeInactive");
if (session.hasContentPending) {
NSLog(@"inactive w/ pending content");
}
}
- (void)sessionDidDeactivate:(WCSession *)session{
// Begin the activation process for the new Apple Watch.
[[WCSession defaultSession] activateSession];
//perform any final cleanup tasks related to closing out the previous session.
}
- (void)sessionReachabilityDidChange:(WCSession *)session{
NSLog(@"sessionReachabilityDidChange");
}
最后一件事,编写适当的方法签名,如果您需要从手表发送数据的回复,请获取有回复的方法签名:...根据苹果以下方法
sendMessage:replyHandler:errorHandler:, sendMessageData:replyHandler:errorHandler:, and transferCurrentComplicationUserInfo:
具有更高的优先级并立即传输。您的应用程序收到的所有消息都在后台线程上串行传递给会话委托。
因此,不要浪费时间在 iOS appDelegate 上的 mainQueue 上调度回复对象,等到您在 watchOS 上获得响应并将其更改为主线程以相应地更新您的 UI。
检查代表连接是否正确?
WCSession* session = WCSession.defaultSession;
session.delegate = self;
[session activateSession];
注意:验证session.delegate = self;
设置为自我。
我发现将回复代码作为运行的第一件事可以解决此问题(可能是由超时引起的?)。
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: ([String : Any]) -> Void) {
print("Message - received")
//Send reply
let data = ["receivedData" : true]
replyHandler(data as [String : AnyObject])
}
在应用程序上工作并具有完全相同的行为。我相当确定我在我的代码中到处查看,并没有发现任何错误。我最好的猜测是,这一定是WatchConnectivity
.
我当前的错误处理程序解决方法只是尝试重新加载有关此特定错误的数据。不是很漂亮,但它工作正常。
你可能想尝试类似的东西?
func messageErrorHandler(error: NSError) {
isLoading = false
print("Error Code: \(error.code)\n\(error.localizedDescription)")
// TODO: WTF?. Check future releases for fix on error 7014, and remove this...
if error.code == 7014 {
// Retry after 1.5 seconds...
retryTimer = NSTimer.scheduledTimerWithTimeInterval(
NSTimeInterval(1.5), target: self, selector: "reloadData", userInfo: nil, repeats: false)
return
}
displayError("\(error.localizedDescription) (\(error.code))",
message: "\(error.localizedDescription)")
}
更新:
对于任何与WatchConnectivity
; 我需要有一个类似的“hack”来测试session.reachable
变量。
我注意到我的应用程序设法在会话可访问之前发送消息。所以我只是尝试重新加载数据(重新发送消息)几次,然后才真正告诉用户他们的手机遥不可及。
更新2:上面的例子是使用.sessionWatchStateDidChange()
,所以问题不是.sendMessage()
因为不等待连接确认而被触发得太早。这一定是一个错误,因为它并非每次都发生,它只是每 100 条消息中就有 1 条。
在 Swift 3 中,我didReceiveMessage
用这个签名解决了实现:
func session(_ session: WCSession, didReceiveMessage message: [String : Any],
replyHandler: @escaping ([String : Any]) -> Void)
您可能需要(检查并)实现您的WCSession
委托实现了以下方法。由于缺少实现,我收到此错误。
- (void)session:(WCSession * _Nonnull)session
didReceiveMessage:(NSDictionary<NSString *, id> * _Nonnull)replyMessage
replyHandler:(void (^ _Nonnull)(NSDictionary<NSString *, id> * _Nonnull replyMessage))replyHandler
{
NSLog(@"Received. %@", replyMessage);
[self processResponse:replyMessage];
}