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 в фоновом режиме.
Любая идея, как обойти эту проблему или исправить ее? Спасибо!
Ответы
Ответ 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
приведет к ошибкам, когда приложение будет убито