Задний план
Прежде всего, давайте подытожим то, что мы знаем. У нас есть
- приложение, работающее на iPhone (далее я буду называть его приложением для iPhone)
- app that runs on Watch...specifically
- UI that runs on Watch
- код, который работает на iPhone как расширение.
Для нас наиболее важны первая и последняя строки. Да, расширение поставляется в AppStore вместе с вашим приложением для iPhone, однако эти две вещи могут работать отдельно в операционной системе iOS. Следовательно, расширение и приложение для iPhone — это два разных процесса — две разные программы, работающие в ОС.
Из-за этого мы не можем использовать [NSNotificationCenter defaultCenter]
, потому что при попытке NSLog()
defaultCenter на iPhone и defaultCenter в расширении они будут иметь разные адреса памяти.
Дарвин в помощь!
Как вы можете себе представить, такая проблема не нова для разработчиков, ее правильный термин — межпроцессное взаимодействие. Итак, в OS X и iOS есть... механизм уведомлений Дарвина. И самый простой способ его использования — реализовать несколько методов из класса CFNotificationCenter
.
Пример
При использовании CFNotificationCenter вы увидите, что он очень похож на NSNotificationCenter. Я предполагаю, что NSNotif.. был построен вокруг CFNotif.. но я не подтвердил эту гипотезу. Теперь к делу.
Итак, давайте предположим, что вы хотите отправить уведомление с iPhone на Watch туда и обратно. Первое, что мы должны сделать, это зарегистрироваться на уведомления.
- (void)registerToNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceivedNSNotification) name:@"com.example.MyAwesomeApp" object:nil];
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), didReceivedDarwinNotification, CFSTR("NOTIFICATION_TO_WATCH"), NULL, CFNotificationSuspensionBehaviorDrop);
}
Вам, наверное, интересно, почему я добавил наблюдателя для NSNotificationCenter? Чтобы выполнить нашу задачу, нам нужно создать некоторый цикл, вы увидите это через мгновение.
Что касается второго способа.
CFNotificationCenterGetDarwinNotifyCenter()
— получить Центр уведомлений Дарвина
(__bridge const void *)(self)
- наблюдатель уведомлений
didReceivedDarwinNotification
- метод callBack, срабатывает, когда объект получает уведомление. В основном это то же самое, что и @selector
в NSNotification.
CFSTR("NOTIFICATION_TO_WATCH")
- название уведомления, та же история в NSNotification, но здесь нам нужен метод CFSTR для преобразования строки в CFStringRef
И, наконец, последние два параметра object
и suspensionBehaviour
— оба игнорируются, когда мы используем DarwinNotifyCenter.
Круто, вот мы и зарегистрировались как наблюдатели. Итак, давайте реализуем наши методы обратного вызова (их два, один для CFNotificationCenter и один для NSNotificationCenter).
void didReceivedDarwinNotification()
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"com.example.MyAwesomeApp" object:nil];
}
Теперь, как видите, этот метод не начинается с - (void)Name...
. Почему? Потому что это метод C. Вы понимаете, зачем нам здесь нужен NSNotificationCenter? Из метода C у нас нет доступа к self
. Один из вариантов — объявить статический указатель на себя, например так: static id staticSelf
назначьте его staticSelf = self
, а затем используйте из didReceivedDarwinNotification
: ((YourClass*)staticSelf)->_yourProperty
, но я думаю, что NSNotificationCenter — лучший подход.
Итак, в селекторе, который отвечает на ваше NSNotification:
- (void)didReceivedNSNotification
{
// you can do what you want, Obj-C method
}
Когда мы, наконец, зарегистрируемся как наблюдатель, мы сможем отправить что-то из приложения для iPhone.
Для этого нам нужна всего одна строка кода.
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("NOTIFICATION_TO_WATCH"), (__bridge const void *)(self), nil, TRUE);
который может быть в вашем ViewController или Model.
Опять же, мы хотим получить CFNotificationCenterGetDarwinNotifyCenter()
, затем указываем имя для уведомления, объект, который публикует уведомление, объект словаря (игнорируется при использовании DarwinNotifyCenter, и последние параметры являются ответом на вопрос: доставить немедленно?
Аналогичным образом вы можете отправить уведомление с часов на iPhone. По понятным причинам я предлагаю использовать другое имя уведомления, например CFSTR("NOTIFICATION_TO_IPHONE")
, чтобы избежать ситуации, когда, например, iPhone отправляет уведомление на часы и себе.
Подводить итоги
MMWormhole
— прекрасный и хорошо написанный класс, даже с тестами, которые охватывают большую часть, если не весь код. Его легко использовать, просто не забудьте предварительно настроить группы приложений. Однако, если вы не хотите импортировать сторонний код в свой проект или не хотите использовать его по какой-либо другой причине, вы можете использовать реализацию, представленную в этом ответе. Особенно, если вы не хотите/нужно обмениваться данными между iPhone и Watch.
Есть и второй хороший проект LLBSDMessaging
. Он основан на сокетах Беркли. Более сложный и основанный на более низкоуровневом коде. Вот ссылка на длинный, но хорошо написанный пост в блоге, там вы найдете ссылку на Github. http://ddeville.me/2015/02/interprocess-communication-on-ios-with-berkeley-sockets/.
Надеюсь, это поможет.
person
lvp
schedule
02.03.2015