WCSession sendMessage:replyHandler error code 7014 (WCErrorCodeDeliveryFailed) 通过WCSession发送消息,如果无法成功发送,则会返回错误代码7014(WCErrorCodeDeliveryFailed)。

25
我有一个Watch OS 2应用程序,通过WCSession方法sendMessage:replyHandler:errorHandler:与iOS应用程序通信。

iOS应用程序正确回复,但有时我会收到域代码为WCErrorDomain,代码为7014的错误:“无法传递有效负载”

当iOS应用程序不在前景时,这种情况发生得更频繁。

我找不到任何解决此问题的方法,希望你们中的某个人知道如何解决此问题。


当错误返回时,它是否有任何潜在的错误 (error.userInfo[NSUnderlyingErrorKey])?看到发送消息的代码和应该接收它的委托方法的实现会很好! - ccjensen
嗨,你修复了这个问题吗? - Vishnu Kumar. S
嗨,不,我还在寻找解决方案。 - gsempe
你从未回答过我上面的问题。另外,看到你设置WCSession的类会很好。 - ccjensen
你能解决这个问题吗?我在Watch OS 3 beta 6上遇到了这个问题。如果你找到了解决方案,请帮忙一下。 - Infaz
显示剩余2条评论
13个回答

16

在我的情况下,我需要实现两个委托:

  1. 一个没有任何replyHandler

    func session(_ session: WCSession,
                     didReceiveMessage message: [String : Any])
    
  2. 带有 replyHandler 的那个 元素

  3. func session(_ session: WCSession,
                 didReceiveMessage message: [String : Any],
                 replyHandler: @escaping ([String : Any]) -> Void)
    

如果您发送一个没有 replyHandler 的消息,则会运行第一个委托。
如果您发送一个带有 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
}

观察OS 4


13

如果你在使用Swift3的iOS10 beta 6和GM版本时遇到了问题,解决方法是修改iOS应用程序中的委托函数头如下:

    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {

请注意 @escaping 和 Any 类型而不是 AnyObject 类型。


你是一个超级英雄! - Ahmadreza

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){

                }
            }
        }
    }
}

希望这能对你有所帮助 :)

3
抱歉,我的声望不够高,无法评论答案。 我的问题已经得到了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])
}

1
我曾经遇到过同样的问题,将WCSession的初始化(设置代理并激活)推迟到应用程序生命周期的后期可以解决该问题。
我曾在应用程序代理的didFinishLaunching中激活了WCSession,但在那里进行此操作会导致通信出现问题。将WCSession的初始化推迟到应用程序的后期可以让通信再次正常工作。

1
在我的情况下,我将WCSessionDelegate(iOS端)放在一个单独的类中,并将其初始化为局部变量。将其更改为全局实例变量解决了这个问题。
所以我的iOS代码是:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
     SessionHandler()
}

将其翻译为中文:"

更改如下以使其正常工作:

",保留HTML标记,不做解释。
var handler: SessionHandler!

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
     handler = SessionHandler()
}

0

正在开发一个应用程序,遇到了完全相同的问题。 我相当确定已经在我的代码中到处查找并没有发现任何错误。我最好的猜测是这一定是 WatchConnectivity 的一个 bug。

我的当前错误处理程序解决方法只是尝试在出现此特定错误时重新加载数据。不是很美观,但它可以正常工作。

你可能想尝试类似的解决方案?

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次。


为什么不使用委托回调通知您可达性已更改或使用可达性属性上的KVO来了解何时再次尝试发送? - ccjensen
我正在使用它,上面的代码片段只是我的'.sendMessageData()'错误处理程序。 - Mikael Hellman

0
您可能需要(检查并)实现您的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];
}

0
在Swift 3中,我通过实现以下签名的didReceiveMessage方法来解决问题:
func session(_ session: WCSession, didReceiveMessage message: [String : Any],
             replyHandler: @escaping ([String : Any]) -> Void)

0

检查委托是否连接正确?

 WCSession* session = WCSession.defaultSession;
 session.delegate = self;
 [session activateSession];

注意:验证 session.delegate = self; 是否设置为 self。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接