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
Имаме подобен проблем, изпращайки userInfo от часовник към телефон и понякога делегатът изобщо не се обажда. Преди 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 е виновен.

Открих, че за да могат съобщенията ми да преминават надеждно, трябваше да проверя и за достижимост и за activationState. Тъй като също исках да продължа да поддържам по-ранни версии на 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)
            }
        }
    }
}

Правенето на тези промени изглежда е разрешило проблемите, които виждах. Интересувам се да видя дали те помагат за разрешаването на нечии други проблеми или все още има проблеми, свързани с transferUserInfo, който не работи.

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 вече се извиква в главната нишка. ErrorHandler може да не е, но разбирам, че е безопасно да се извика 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