Приложения Apple Watch, которым требуются обновления данных, такие как информация об акциях, погоде, спорте и транспорте, представляют методы связи между родительским приложением iOS и расширением WatchKit.
В этой статье я покажу, как создать это общение и представить обмен сообщениями в реальном времени между ними.
Давайте рассмотрим обзор связи между приложением iOS и его расширением WatchKit, а также различными состояниями приложения iOS и расширения WatchKit, когда вам нужно обмениваться данными.
openParentApplication: ответ:
В бета-версии WatchKit 2 компания Apple представила openParentApplication:reply:
и соответствующее application:handleWatchKitExtensionRequest:reply
метод UIApplicationDelegate. Метод openParentApplication:reply:
может вызываться из любого контроллера интерфейса WatchKit, а application:handleWatchKitExtensionRequest:reply
будет вызываться из файла AppDelegate приложения iOS.
openParentApplication:reply:
может передавать словарь в главное приложение, которое может включать любые данные, на которые родительское приложение iOS должно отвечать. Это позволяет расширению WatchKit пробуждать содержащее приложение iOS от Apple Watch, если оно приостановлено или не запущено:
[WKInterfaceController openParentApplication:@{@"action" : @"actionName"} reply:^(NSDictionary *replyInfo, NSError *error) { if (error) { NSLog(@"An error happened while opening parent application: %@", error); } else { //do something.. } }];
После запуска application:handleWatchKitExtensionRequest:reply:
iOS приложение application:handleWatchKitExtensionRequest:reply:
передает словарь, отправленный с openParentApplication:reply:
Внутри handleWatchKitExtensionRequest:reply:
вам нужно будет настроить любые действия, которые должно запускать приложение iOS. Например, отправка HTTP-запроса RESTful или что-либо, что необходимо для ответа на расширение WatchKit. Когда вы закончите, вы должны вызвать блок reply
чтобы система знала, что вы выполнили любую задачу, которую нужно выполнить, чтобы завершить ответ:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply { //do some stuff //send back a dictionary of data to the watchkit extension reply(@{@"dataToReturn": @"dataToReturnValue"}) }
Блок reply
позволяет приложению iOS возвращать данные в расширение WatchKit.
Этот метод может помочь, например, если вы разрабатываете музыкальное приложение, которое будет работать в фоновом режиме, и вы хотите управлять им из Apple Watch. Вы будете использовать openParentApplication:reply:
и просто использовать словарь для передачи команды паузы, остановки или возобновления.
Одно важное замечание: если родительское приложение iOS еще не запущено, оно прекратит работу после того, как вы ответите на запрос. Если, например, вы не включите фоновый режим для запуска обновления местоположения или аудио. Метод openParentApplication:reply:
запустит приложение при необходимости, но не выведет его на передний план. Приложение запускается в фоновом режиме, и это означает, что невозможно заставить интерфейс отображаться, если его еще нет на экране.
NSUserDefaults
Другой способ создания связи между приложением iOS и его расширением WatchKit — использование NSUserDefaults
. NSUserDefaults — это API, который располагается поверх plist-файла. Концепция групп приложений
была представлена с выпуском расширений в iOS 8. Родительское приложение и расширение iOS могут обмениваться данными через общую изолированную программную среду, делая их частью одной группы. NSUserDefaults имеет конструктор initWithSuite:
позволяет сохранять и читать данные с помощью API NSUserDefaults между исполняемыми файлами.
Помимо изменения метода init
, ничто другое не изменится от NSUserDefaults, к которым вы привыкли. В приложении для iPhone вы можете установить данные, которые нужно хранить в NSUserDefaults:
NSUserDefaults *defaults = [NSUserDefaults initWithSuite:@"appGroupName"]; [defaults setInteger:4 forKey@"myKey"]; [defaults synchronize];
Внутри расширения WatchKit вы можете читать данные в любое время:
NSUserDefaults *defaults = [NSUserDefaults initWithSuite:@"appGroupName"]; NSInteger myInt = [defaults integerForKey@"myKey"];
Это отличный метод для передачи данных между расширением WatchKit и его родительским приложением iOS, особенно если вам не нужно будить родительское приложение для просмотра данных. Обратите внимание, что вы можете сериализовать любой тип данных в NSUserDefaults. Это идеально подходит для обмена данными с виджетом Today, в котором нет возможности открыть родительское приложение и запросить данные.
Дарвин Центр уведомлений
В некоторых ситуациях требуется уведомление при изменении значения, а предыдущие методы не достигают этого. В родительском приложении iOS могут быть данные, которые изменяются, когда пользователь использует приложение WatchKit. В ситуациях, когда требуется обновление данных в режиме реального времени, Apple рекомендовала использовать Центр уведомлений Darwin .
Есть популярная библиотека, которую я использовал для подобных ситуаций, она называется MMWormhole . MMWormhole поддерживает CFNotificationCenter Darwin Notifications для поддержки уведомлений об изменениях в реальном времени. MMWormhole использует NSKeyedArchiver в качестве средства сериализации, поэтому любой объект, совместимый с NSCoding, может работать как сообщение. Сообщения могут быть отправлены и сохранены в виде архивных файлов и прочитаны позже, когда приложение или расширение активируется. MMWormhole работает вне зависимости от того, запущено приложение или нет, но уведомления в приложении содержат только уведомления, если приложение неактивно в фоновом режиме. Это делает MMWormhole идеальным для случаев, когда содержащее приложение уже работает в некоторой форме фоновых режимов.
MMWormhole зависит от наличия заинтересованных сторон, которые могут прослушивать и получать уведомления о сообщениях, отправленных другими сторонами. Эффект почти мгновенного обновления с обеих сторон, когда сообщение отправляется через червоточину. Использование MMWormhole очень похоже на использование NSUserDefaults и использует ту же конфигурацию группы приложений
.
Допустим, у нас есть приложение для iOS, обновляющее оставшееся время для прибытия шины, и вам необходимо мгновенно обновить те же данные в приложении Apple Watch. В приложении для iOS мы можем настроить червоточину и обновлять оставшееся время, когда оно меняется:
self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:@"myAppGroup" optionalDirectory:@"wormhole"]; //whenever the time changes... [self.wormhole passMessageObject:currentSpeed identifier:@"currentRemainingTime"];
С расширением WatchKit мы можем обновить текущее оставшееся время внутри метода init
. В отличие от UIKit для iOS, представления загружаются и проверяются, когда init
вызывается из WKInterfaceController
:
- (instancetype)init { if (self = [super init]) { self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:@"myAppGroup" optionalDirectory:@"wormhole"]; NSNumber *currentRemainingTime = [self.wormhole messageWithIdentifier:@"currentRemainingTime"]; [self.remainingTimeLabel setText:[NSString stringWithFormat:@"%@ Seconds", currentRemainingTime]; } return self; }
Это близко к тому, как мы делаем вещи с NSUserDefaults. В некоторых случаях Apple Watch отключает экран, когда пользователь не взаимодействует с ним (для экономии энергии). Это сделает наше приложение Apple Watch деактивированным. Когда пользователь снова нажимает на наше приложение и начинает использовать его, состояние приложения Apple Watch снова активируется. В этом случае мы должны снова начать прослушивание любых входящих сообщений, потому что в приведенном выше примере мы выполнили свою работу в методе init
, но данные могут измениться за кулисами после init
. В этой ситуации используйте методы willActivate
и didDeactivate
в WKInterfaceController
чтобы узнать, когда нам следует обновить данные. С помощью этих методов мы можем получить любые изменения, но только тогда, когда наше представление на экране и экран просмотра включен:
- (void)willActivate { // This method gets called when watch view controller is about to be visible to user [super willActivate]; [self.wormhole listenForMessageWithIdentifier:@"currentSpeed" listener:^(id messageObject) { [self.topSpeedLabel setText:[NSString stringWithFormat:@"%@ MPH", (NSNumber *)messageObject]; }]; } - (void)didDeactivate { // This method gets called when watch view controller is no longer visible [super didDeactivate]; [self.wormhole stopListeningForMessageWithIdentifier:@"currentSpeed"]; }
Это означает, что когда экран выключен (наше приложение может быть еще живым, но пустым экраном), мы не хотим тратить силы на связь с родительским приложением iOS и обновление пользовательского интерфейса, который пользователь не видит.
В следующей части этого короткого цикла я буду с нуля кодировать небольшое приложение для списка дел, которое реализует различные методы связи, описанные выше. А пока, дайте мне знать, если у вас есть какие-либо вопросы в комментариях ниже.