WCSession sendMessage: replyHandler код ошибки 7014 (WCErrorCodeDeliveryFailed)

У меня есть приложение Watch OS 2, которое взаимодействует с приложением iOS с помощью метода WCSession sendMessage:replyHandler:errorHandler:

Ответ приложения iOS корректно, но время от времени я получаю сообщение об ошибке с кодом 7014 домена WCErrorDomain: "Полезная нагрузка не может быть доставлена"

Это происходит чаще, когда приложение iOS не является передним.

Я не нахожу решения этой проблемы, надеюсь, что один из вас знает решение этой проблемы

Ответы

Ответ 1

Для тех, кто имеет проблемы с iOS10 beta 6 и GM, и вы используете Swift3, решение состоит в том, чтобы изменить заголовок функции делегата в приложении iOS на следующее:

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

Обратите внимание на @escaping и Any, а не на тип AnyObject.

Ответ 2

Попробуйте это, это исправило мою проблему. Внутри 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];

    }
}

В телефоне добавьте следующие коды для получения данных из часов.

Добавьте следующее в doneFinishLaunchingWithOptions.

// 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

Извините, у меня недостаточно репутации, чтобы комментировать ответы. Моя проблема решена с Питером Робертом: С Swift 3 появился WCErrorCodeDeliveryFailed, и решение просто меняло AnyObject на Any на answerHandlers.

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

Ответ 4

Я переживал то же самое и перемещаю инициализацию WCSession (установка делегирования и активация), позже в жизненном цикле приложения исправлена ​​проблема.

У меня была активизация WCSession в делегатах приложения didFinishLaunching, и там она сломала связь. Перемещение инициализации WCSession позже в приложении сделало сообщение снова.

Ответ 5

Работа с приложением и его точное поведение. Я чувствую себя уверенно, что я везде искал свой код и не нашел ничего плохого. Мое лучшее предположение состоит в том, что это должно быть ошибкой с 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)")
}

UPDATE:

Для тех, кто работает с WatchConnectivity; Мне нужно иметь подобный "взлом" для тестирования переменной session.reachable.

Я заметил, что моему приложению удается отправить сообщение до того, как сеанс станет доступным. Поэтому я просто пытаюсь перезагрузить данные (повторно отправить сообщение) пару раз, прежде чем фактически сообщить пользователю, что их телефон недоступен.

ОБНОВЛЕНИЕ 2: В приведенном выше примере используется .sessionWatchStateDidChange(), поэтому проблема заключается не в том, что .sendMessage() запускается слишком рано из-за того, что не ждет соединения ack. Это должно быть ошибкой, поскольку это не происходит каждый раз, это просто волнует, как 1 на 100 сообщений.

Ответ 6

Вам может потребоваться (проверить и) реализовать, чтобы ваш делегат 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];
}

Ответ 7

Проверьте правильность подключения делегата?

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

Примечание. Убедитесь, что session.delegate = self; установлен в self.

Ответ 8

Я обнаружил, что включение кода ответа в первую очередь запускает исправления этой проблемы (возможно, вызвано тайм-аутом?).

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

}

Ответ 9

В Swift 3 я решил реализовать didReceiveMessage с этой сигнатурой:

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

Ответ 10

Убедитесь, что сессия всегда активна. У меня, например, был другой вид, который был частью тестирования, а затем возвращен на начальном представлении и задавался вопросом, почему сеанс больше неактивен.

- (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, глупо мне....

Ответ 11

Этот сценарий будет охватывать несколько вариантов использования. Пожалуйста, взгляните на эти шаги, это очень помогает мне.

1 - Поймите, что каждое устройство должно иметь собственный экземпляр WCSession и настроить соответствующие делегаты.

2 - реализовать WCSessionDelegate только в одном месте на каждом устройстве, ej. в приложении iOS на AppDelegate, на watchOS на ExtensionDelegate. Это очень важно, потому что с соответствующим WCSession, настроенным на watchOS, но на iPhone реализуется на двух разных местах, ej. на делете приложения, а затем на первом viewcontorllweer приложения (в моем случае) приводят к нестабильному поведению, и это главная причина, по которой иногда приложение iOS перестает отвечать на сообщение, полученное от часов.

3 - активировать сеанс рекомендуется только в Host App. Это пример моего приложения iOS только с одним WCSessionDelegate. (AppDelegate)


#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: 

имеет более высокий приоритет и сразу передается. Все сообщения, полученные вашим приложением, доставляются делегату сессии поочередно по фоновому потоку.

Поэтому не тратьте время на отправку объекта ответа на mainQueue в приложении iOS appDelegate, подождите, пока вы ответите на ваш watchOS, и измените его на главный поток, чтобы соответствующим образом обновить свой интерфейс.