Ответ 1
Фон
Прежде всего, подведите итог тому, что мы знаем. Мы имеем
- приложение, которое работает на iPhone (я буду называть его как приложение для iPhone)
- приложение, которое работает на Watch... специально
- Пользовательский интерфейс, который работает на Watch
- который работает на iPhone как расширение.
Для нас важны первая и последняя строки. Да, приложение добавлено в AppStore с вашим iPhone-приложением, однако эти две вещи могут выполняться отдельно в операционной системе iOS. Следовательно, приложение расширения и iPhone - это два разных процесса - две разные программы, работающие в ОС.
Из-за этого мы не можем использовать [NSNotificationCenter defaultCenter]
, потому что, когда вы пытаетесь выполнить NSLog()
defaultCenter на iPhone и defaultCenter в Extension, у них будет другой адрес памяти.
Дарвин на помощь!
Как вы можете себе представить, такая проблема не нова для разработчиков, поэтому ее термином является Interprocess Communication. Так что в OS X и iOS есть механизм уведомления Дарвина. И самый простой способ использовать его - реализовать несколько методов из класса CFNotificationCenter
.
Пример
При использовании CFNotificationCenter вы увидите, что он очень похож на NSNotificationCenter. Я думаю, NSNotif.. был построен вокруг CFNotif.. но я не подтвердил эту гипотезу. Теперь, к сути.
Итак, давайте предположим, что вы хотите отправить уведомление с iPhone, чтобы смотреть туда и обратно. Первое, что мы должны сделать, это зарегистрироваться на уведомления.
- (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...
. Зачем? Потому что это метод С. Вы видите, почему нам нужен 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 и последние параметры ответ на вопрос: доставить немедленно?
Аналогичным образом вы можете отправлять уведомления от Watch to iPhone. По очевидной причине я предлагаю использовать другое имя уведомления, например CFSTR("NOTIFICATION_TO_IPHONE")
, чтобы избежать ситуации, когда, например, iPhone отправляет уведомление на Watch и на себя.
Подводя итог
MMWormhole
- это прекрасный и хорошо написанный класс, даже с тестами, которые охватывают большинство, если не все, код. Он прост в использовании, просто не забудьте настроить свои AppGroups раньше.
Однако, если вы не хотите импортировать сторонний код в свой проект или вы не хотите использовать его по какой-либо другой причине, вы можете использовать реализацию, представленную в этом ответе.
Особенно, если вы не хотите/нуждаетесь в обмене данными между iPhone и Watch.
Существует также второй хороший проект LLBSDMessaging
. Это основано на гнездах Беркли. Более сложный и основанный на более низкоуровневом коде. Вот ссылка на длинную, но хорошо написанную запись в блоге, там вы найдете ссылку на Github. http://ddeville.me/2015/02/interprocess-communication-on-ios-with-berkeley-sockets/.
Надеюсь на эту помощь.