TransferUserInfo WCSession больше не работает надежно в watchOS 2.2 с iOS 9.3

У меня есть существующее приложение iOS 9.2 и watchOS 2.1, которое использует sendMessage и transferUserInfo для отправки данных с iPhone на Apple Watch. Если sendMessage терпит неудачу, я использую transferUserInfo для постановки данных в очередь для последующей доставки:

// *** In the iOS app ***
self.session.sendMessage(message, replyHandler: nil) { (error) -> Void in
    // If the message failed to send, queue it up for future transfer
    self.session.transferUserInfo(message)
}

// *** In the watchOS app ***
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
    // Handle message here
}

func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
    // Handle message here
}

Без изменения какого-либо кода и запуска приложения на iOS 9.3 с watchOS 2.2 на реальном устройстве (симулятор не имеет такой проблемы), sendMessage передает данные на Apple Watch, пока часы находятся в пределах досягаемости и экран включен. Это ожидаемо и как это работало ранее. Однако, если экран выключен и sendMessage не работает, transferUserInfo больше не передает данные на Apple Watch, когда экран снова включается.

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

func session(session: WCSession, didFinishUserInfoTransfer userInfoTransfer: WCSessionUserInfoTransfer, error: NSError?) {
    // Called when self.session.transferUserInfo completes
}

Этот метод вызывается после вызова transferUserInfo, но ошибка не возвращается, и приложение iOS, похоже, указывает, что передача произошла успешно.

Сначала я подумал, что, возможно, время передачи данных увеличилось, но после того, как я оставил устройство в покое на сутки, данные так и не были переданы. Теперь я несколько подозреваю, что это как-то связано с новым API для нескольких часов, и, возможно, приложению iOS нужно знать конкретные часы, чтобы отправить их, хотя у меня когда-либо были сопряжены только одни часы. У кого-нибудь есть идеи, что могло измениться и как правильно использовать transferUserInfo?


person lehn0058    schedule 29.03.2016    source источник
comment
У нас аналогичная проблема: мы отправляем информацию о пользователе с часов на телефон, а иногда делегируем, что нам вообще не звонят. До 9.3 и 2.2 это работало.   -  person Ultrakorne    schedule 29.03.2016
comment
взгляните на этот пост forums.developer.apple.com/thread/43596   -  person Ultrakorne    schedule 29.03.2016
comment
Я второй пользовательский комментарий :) Этот пост заставляет меня еще больше подозревать новые API для нескольких часов.   -  person lehn0058    schedule 29.03.2016
comment
Я просто играл с transferCurrentComplicationUserInfo и заметил то же самое. Это определенно похоже на новую ошибку, представленную Apple.   -  person lehn0058    schedule 29.03.2016
comment
У меня все ломается после того, как я удаляю приложение на телефоне, а затем переустанавливаю его. После этого transferCurrentComplicationUserInfo:, похоже, так и не смог успешно передать полезную нагрузку userInfo на часы. Только когда я перезагружаю часы, все снова начинает работать.   -  person ospr    schedule 07.04.2016


Ответы (2)


Я думаю, что у меня это работает сейчас. Во-первых, мне пришлось добавить новые методы WCSessionDelegate в мое приложение для iOS:

@available(iOS 9.3, *)
func session(session: WCSession, activationDidCompleteWithState activationState: WCSessionActivationState, error: NSError?) {
    if activationState == WCSessionActivationState.Activated {
        NSLog("Activated")
    }

    if activationState == WCSessionActivationState.Inactive {
        NSLog("Inactive")
    }

    if activationState == WCSessionActivationState.NotActivated {
        NSLog("NotActivated")
    }
}

func sessionDidBecomeInactive(session: WCSession) {
    NSLog("sessionDidBecomeInactive")
}

func sessionDidDeactivate(session: WCSession) {
    NSLog("sessionDidDeactivate")

    // Begin the activation process for the new Apple Watch.
    self.session.activateSession()
}

И аналогично моему приложению watchOS:

@available(watchOSApplicationExtension 2.2, *)
func session(session: WCSession, activationDidCompleteWithState activationState: WCSessionActivationState, error: NSError?) {
    if activationState == WCSessionActivationState.Activated {
        NSLog("Activated")
    }

    if activationState == WCSessionActivationState.Inactive {
        NSLog("Inactive")
    }

    if activationState == WCSessionActivationState.NotActivated {
        NSLog("NotActivated")
    }
}

Но у меня по-прежнему не работала функция TransferUserInfo, особенно когда экран Apple Watch был выключен. Ниже показано, как я отправлял информацию между iPhone и Apple Watch в iOS 9.2/watchOS 2.1:

func tryWatchSendMessage(message: [String : AnyObject]) {
    if self.session != nil && self.session.paired && self.session.watchAppInstalled {
        self.session.sendMessage(message, replyHandler: nil) { (error) -> Void in
            // If the message failed to send, queue it up for future transfer
            self.session.transferUserInfo(message)
        }
    }
}

Я предположил, что отправка сообщения с iPhone на Apple Watch, когда экран часов был выключен, приводила к сбою TransferUserInfo, потому что оно было в обработчике ошибок sendMessage. sendMessage также работал, как и ожидалось, когда экран был включен. Однако похоже, что обработчик ответа об ошибке sendMessage не всегда вызывается, если экран ваших часов выключен, даже если запрос не выполняется. Это отличается от предыдущих версий ОС. Это также, по-видимому, вызвало каскадный эффект, когда последующие сообщения также не сработали, даже если условия были подходящими. Это то, что заставило меня поверить, что виноват TransferUserInfo.

Я обнаружил, что для того, чтобы мои сообщения проходили надежно, мне нужно было проверить как состояние доступности, так и состояние активации. Поскольку я также хотел продолжить поддержку более ранних версий iOS и watchOS, мой метод tryWatchSendMessage стал следующим:

func tryWatchSendMessage(message: [String : AnyObject]) {
    if #available(iOS 9.3, *) {
        if self.session != nil && self.session.paired && self.session.watchAppInstalled && self.session.activationState == .Activated {
            if self.session.reachable == true {
                self.session.sendMessage(message, replyHandler: nil) { (error) -> Void in
                    // If the message failed to send, queue it up for future transfer
                    self.session.transferUserInfo(message)
                }
            } else {
                self.session.transferUserInfo(message)
            }
        }
    } else {
        // Fallback on earlier versions
        if self.session != nil && self.session.paired && self.session.watchAppInstalled {
            if self.session.reachable == true {
                self.session.sendMessage(message, replyHandler: nil) { (error) -> Void in
                    // If the message failed to send, queue it up for future transfer
                    self.session.transferUserInfo(message)
                }
            } else {
                self.session.transferUserInfo(message)
            }
        }
    }
}

Внесение этих изменений, кажется, решило проблемы, которые я видел. Мне интересно посмотреть, помогут ли они решить чьи-либо проблемы, или все еще есть проблемы, связанные с передачейUserInfo, которая не работает.

person lehn0058    schedule 30.03.2016
comment
Хорошая находка ... Хотя я немного смущен вашим первоначальным вопросом. Кажется, вы не изменили логику, связанную с transferUserInfo, кроме как для правильной защиты sendMessage в 9.3? - person Cobra; 31.03.2016
comment
Интересно, есть ли на самом деле проблема с потоками. Насколько я понимаю, WCSession является синглтоном. В этом случае ответ и обработка ошибок будут происходить в фоновом потоке. Если вы выдаете sendMessage из этих блоков, возможно, они выполняются параллельно с основным потоком. Вы пытались поставить свой sendMessage в очередь в основной поток вместо того, чтобы запускать его в фоновом режиме, как это? - person Cobra; 31.03.2016
comment
Для меня tryWatchSendMessage всегда вызывается в основном потоке, поэтому sendMessage уже вызывается в основном потоке. Обработчик ошибок может и не быть, но насколько я понимаю, вызывать transferUserInfo извне основного потока безопасно. - person lehn0058; 31.03.2016
comment
Вам следует просмотреть документацию по sendMessage. Обработчики блоков запускаются в фоновом потоке (по крайней мере, согласно Apple), это будет включать ваш код replyHandler, верно? - person Cobra; 31.03.2016
comment
Мои обработчики ответов равны нулю. Проблема заключалась в том, что обработчик ошибок даже не вызывался, поэтому я даже не мог вызвать TransferUserInfo в фоновом потоке. - person lehn0058; 31.03.2016
comment
Вы решили, что это не связано с тем, что transferUserInfo было отправлено, но не доставлено. (Возможно, стоит обновить ветку форума разработчиков, чтобы прояснить этот отчет!) Хотя ваш новый код в первую очередь избегает срабатывания ошибки reachable, обработчик ошибок должен ее улавливать: был недоступен, но могут возникнуть и другие ошибки. Любопытный. Возможно, вы могли бы задать новый вопрос, чтобы конкретно определить, почему ваш блок обработчика ошибок iOS не вызывается? - person ; 01.04.2016
comment
Согласен, настоящая ошибка, которую я видел, заключается в том, что обработчик ошибок не вызывается надежно в sendMessage. - person lehn0058; 01.04.2016
comment
Он работает с симулятором iPhone для просмотра симулятора, но не работает с симулятора часов на симулятор iPhone. Можете ли вы помочь мне ? - person Rahul Vyas; 06.03.2020

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

Как и у всех, в симуляторе заработало, а на устройстве нет.

Сегодня я заметил, что пытаюсь отправить данные из фонового потока — я завернул все свои вызовы в dispatch_async(dispatch_get_main_queue()), и вдруг теперь все работает.

person zerogeek    schedule 04.04.2016