Подтвердить что ты не робот

WatchOS2 WCSession sendMessage не разбудит iPhone на фоне

Это тестируется как на симуляторе, так и на реальном физическом устройстве iphone5s. Я попытался использовать WCSession sendMessage для связи от WatchOS2 до iPhone iOS9. Он работает хорошо, когда приложение iphone работает либо в режиме переднего плана, либо в фоновом режиме.

Но если я убью приложение для iPhone (вообще не запускаю приложение), тогда я всегда получаю timeHandler timeout. Так что Watch больше не может общаться с iPhone.

"Error Domain = WCErrorDomain Code = 7012" Сообщение ответа заняло слишком много времени. "UserInfo = {NSLocalizedDescription = Сообщение ответа заняло слишком много времени. NSLocalizedFailureReason = время ожидания ответа.}".

Я думаю, что он должен пробудить приложение iPhone в фоновом режиме.

Любая идея, как обойти эту проблему или исправить ее? Спасибо!

4b9b3361

Ответ 1

После нескольких часов попыток и подсказок от @jeron. Я, наконец, сам разобрался с проблемой.

В моей сессии: метод делегирования didReceiveMessage, у меня есть два вызова. 1.replyHandler call. 2. В моем случае у меня запущен процесс async (RXPromise). Он вложил несколько обратных вызовов RXPromise для получения различных данных из облачного сервиса. Я не обращал на это внимания, потому что он должен немедленно позвонить и вернуться. Но теперь, когда я прокомментировал блок RXPromise все вместе, он может пробуждать приложение iOS в фоновом режиме каждый раз.

Наконец, я выясню, что проблема заключается в том, что после вызова RXPromise это не гарантирует, что вы снова приземлитесь в основной поток. И я считаю, сессия: didReceiveMessage должен быть включен в основной поток. Я не упоминал об этом нигде в документации Apple.

Окончательное решение:

- (void)session:(WCSession *)session
    didReceiveMessage:(NSDictionary<NSString *, id> *)message
         replyHandler:(void (^)(NSDictionary<NSString *, id> *_Nonnull))replyHandler {

    replyHandler(@{ @"schedule" : @"OK" });

    dispatch_async(dispatch_get_main_queue(), ^{
      Nested RXPromise calls.....
    });

}

Ответ 2

Важно, чтобы вы активировали WCSession в вашем методе AppDelegate didFinishLaunchingWithOptions. Также вам нужно установить WCSessionDelegate. Если вы делаете это в другом месте, код может не выполняться, когда система запускает убитое приложение в фоновом режиме.

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

Вот пример, который пробуждает приложение, если оно убито:

В WatchExtension:

Настройка сеанса. Обычно в ExtensionDelegate:

func applicationDidFinishLaunching() {
    if WCSession.isSupported() {
        let session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()
    }
}

И затем отправьте сообщение, когда вам нужно что-то из приложения:

if WCSession.defaultSession().reachable {
    let messageDict = ["message": "hello iPhone!"]
    WCSession.defaultSession().sendMessage(messageDict, replyHandler: { (replyDict) -> Void in
        print(replyDict)
        }, errorHandler: { (error) -> Void in
        print(error)
    }
}

В приложении iPhone:

Те же настройки сеанса, но на этот раз также установите делегат:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    ...
    if WCSession.isSupported() {
        let session = WCSession.defaultSession()
        session.delegate = self
        session.activateSession()
    }
}

И затем реализовать метод делегата для отправки ответа на часы:

func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
    replyHandler(["message": "Hello Watch!"])
}

Это работает всякий раз, когда есть связь между Watch и iPhone. Если приложение не запущено, система запускает его в фоновом режиме.

Я не знаю, будет ли система ждать достаточно долго, пока вы не получите свои данные из iCloud, но этот пример определенно пробуждает приложение.

Ответ 3

Ну, вы можете использовать transferUserInfo для очереди вызовов. Использование sendMessage приведет к ошибкам, когда приложение будет убито