Issues in the first version of WatchKit framework, provide real examples on the development of Apple Watch extension.
The topic will include the discussion and comparison of the first and second versions of Apple watchOS architecture. Present a technical information about communication between the main app and Apple Watch. New possibilities of open API for creating Apple Watch native applications.
6. Sharing code & Interprocess Communication
• include model classes, web
services, credentials
• restricted API
• openParentApplication:reply:
• App Groups
• Darwin-notification
1
7. openParentApplication
iPhone Host App watchKit Extension
write seed files to app group
sends user info
gets input from wearer
reads data from seed file
sends user info (include error)
gets input from wearer
reply(<NSCoding>)
updates in-memory data
system wakes up in background
+openParentApplication:reply:
launch
1
9. Darwin-notifications
Interprocess communication in iOS by means of Darwin-notifications.
Sending process
Notification
notifyd
routines allow processes
to exchange stateless
notification events
process 1
process 2
process 3
Client processes
1
10. willActivate
Page-based Controller Life Cycle Changes
Page 1 Page 2 Page 3
Started
init
awakeWithContext:
init
awakeWithContext:
init
awakeWithContext:
NSExtensionHostWillEnterForegroundNotification
NSExtensionHostDidBecomeActiveNotification
willActivate
willActivate
willDeactivate
Page 1 Page 2 Page 3
Swipe to Page 2
willActivate
didDeactivate
willActivate
willDeactivate
Drop arm
NSExtensionHostWillResignActiveNotification
NSExtensionHostDidEnterBackgroundNotification
didDeactivate
Suspended
Drop arm
NSExtensionHostWillEnterForegroundNotification
NSExtensionHostDidBecomeActiveNotification
1
14. Subset of iOS frameworks
Include project frameworks
New watchOS Platform & SDK
UI responsiveness
New UI elements
Animation
Independent operation
Wi-Fi : NSURLSession
15. Features
Architecture
WatchExtension target in main
app
WatchExtension target in watch app
Network access - able to connect to open Wi-Fi networks
Interprocess Com
munication
instable, featureless
methods (openParentApplication,
Darwin)
stabel & clear Connectivity Framework
Data Storage caches folder (image caching) document folder (persistent)
Animation
generate a series of images and
then iterate through them
true IOS animations
(animate properties)
User Activity in root WKInterfaceController in WKExtensionDelegate
Way to show info
WatchApp, Glance, Notification
WatchApp, Glance, Notification,
WatchOS 1 WatchOS 2
21. Watch Connectivity Background transfer
• Queue up content
• OS transfer content
• Information not needed immediately
• Application context (LIFO)
• User Info Transfer (FIFO)
• File Transfer (Once)2
22. Watch Connectivity Interactive messaging
• Replying: optional handler
(confirmation by reciever)
• Live communication
• Dictionary (property list type)
• Data (custom data, own
serialization)
2
23.
24. Local data storage
• non-purgeable, not restored
Document folder
(persistent info)
• Purgeable
Caches folder
(go away at any time)• Image caching
Caches folder
Can’t store persistent info
WatchOS 2WatchOS 1
2
34. Using cocoapods
Specify the deployment target for watchOS in the Podspec:
Pod::Spec.new do |s|
# …
s.watchos.deployment_target = '2.0'
end
Any Pod that can support watchOS needs to explicitly add support
by adding s.watchos.deployment_target.
2
(слайд 1) Добрый день. Меня зовут Владимир, я ios - разработчик в компании Agilie. Мне нравится работать с нестандартными подходами к решению задач. Поэтому когда родилась идея создания нового приложения для Dribbble - это был для меня новый опыт.
переход: я надеюсь что все знают что такое dribbble
(слайд 2) При входе на сайт мы сразу попадаем в раздел Popular. Хочу акцентировать внимание на статистике в категории Popular. Здесь на шотах мы видим большое количество просмотров, лайков и комментариев. И естественно цель каждого дизайнера попасть именно в эту категорию, что подтверждает его популярность. Попасть в категорию Popular может любой начинающий дизайнер, у которого высокие показатели статистики (просмотры, лайки, комментарии). Получив приглашение (invite) дизайнер выкладывает свой первый шот и он попадает в категорию Debut. По статистике, на первом шоте максимальное количество просмотров, лайков и комментариев, потому что он находится в категории Debut. К последующим работам уже нет такого внимания и дизайнер теряется в огромном комьюнити, потому что новые шоты на Dribbble появляются ежеминутно. По количеству фолловеров мы продвигаемся в общем рейтинге дизайнеров, а по количеству лайков - попадаем в категорию Popular. Это главная цель дизайнера, который находится на Dribbble. Быть узнаваемым и заметным.
переход: Основная целевая аудитория нашего приложения-начинающие дизайнеры, которые недавно получили инвайт и хотят стать популярными. У них мало подписчиков, но достойные работы и много энтузиазма. и это главное.
(cлайд 3) Главный функционал приложения заключается в возможности быстро просматривать шоты, лайкать их и фоловить других дизайнеров. Когда пользователь делает свайп вверх, он лайкает работу. Аналогично происходит фолоу, только пользователь свайпает вниз. На шоте подтягивается статистика с Дрибла, аватар пользователя и название. Можно обновлять шоты с помощью кнопки refresh и просматривать самые свежие работы. Так же есть выпадающее меню со списком категорий.
переход:
(слайд 4) При первом запуске приложения пользователь получает определенное количество монет. Когда пользователь выполняет действие - ставит лайк или фоллоу, он получает за это монеты, которые потом использует для продвижения своих работ. За лайк меньше, за фоллоу-больше. Пользователь не может отменить свои действия. Когда накапливается определенное количество монет, можно начинать промоушн своих работ. Для этого необходимо перейти на экран профиля. Помимо информации с Дрибла на экран профиля мы добавили количество монет и кнопку Add to Featured. При нажатии на эту кнопку, шоты пользователя начинают промоутиться, их оценивают и подписываются. За каждый лайк и фолоу с пользователя снимаются монеты. По мере уменьшения количества монет, внимание к профилю и работам растет. После нажатия на кнопку Add to Featured все шоты пользователя попадают в особую категорию Featured. Это новая категория, добавленная в наше приложение, и именно в ней можно промоутить свои шоты. Благодаря этой категории, работы пользователей замечает комьюнити, которое пользуется приложением. Все остальные категории Dribbble остались без изменений.
переход: Главная цель приложения Shot Bucket - повысить личный рейтинг дизайнера и увеличить количество просмотров работ.
(слайд 5) Мы получили конкурентноспособное и качественное приложение для дизайнеров на трех платформах iOS, Android и Apple Watch. Процесс разработки был непростой. Приходилось общаться с серверными разработчиками из dribbble, изменять архитектуру приложения и бороться с недоработками в сыром фреймворка watchKit.
переход: Сегодня я хотел поделиться опытом такой разработки приложения для Apple Watch и iOS.
(слайд 6) Собственно разработку мы начали с регистрации приложения на сайте dribbble.com, вот форма для регистрации. После успешной регистрации мы получаем ключи для работы с Dribbble API (clientID, Client Secret, Client Access Token).
переход: Dribbble использует протокол oauth 2.0 для аутентификации пользователей и нам был необходим стабильный и безопасный client.
(слайд 7) Изначально в разработке мы взяли за основу NXOauth2Client, но в последствии выявили в нем ряд недостатков. Например: при неудачной авторизации (передаем неверный clientID/client secret) тело ошибки необходимо дополнительно передавать в client из NSURLConnection т.е. вносить изменения в библиотеку. Плюс о результате авторизации клиент сообщает через NSNotificationCenter, что приводит к риску непоследовательного выполнения кода.
Поэтому мы остановились на библиотеке от гугл - GMTOAuth2, которая использует блоковые колбэки с результатом авторизации и дает доступ к телу ошибки соединения при неудачной авторизации.
переход: Dribbble API разрабатывался несколькими итерациями поэтому логика обработки ошибок и особых случаев постоянно изменялась
(слайд 8) Так как документация для dribbble API не систематизирована должным образом. Наша команда отобрала здесь все основные ошибки и коды ответов dribbble API. Логика работы некоторых не совсем понятна, например ошибка 204 No content, которая обычно возвращается по запросу веб страницы возвращается на REST запрос об удалении like comment/like shot. Похожая ситуация и с ошибкой 404 Not found. А большинство ограничений разработчики dribbble API собрали в 422 Unprocessable entity ошибку и причиной ошибки может быть как ограничение количества людей (1800), которых может фолловить юзер так и неверно названый параметр или превышение лимита загруженных шотов в день/месяц.
переход: Но самым серьезным препятствием на пути нашей разработки стало ограничение dribbble на количество запросов для каждого clientID.
(слайд 9) Dribble устанавливает строгое ограничение в 60 запросов в минуту или 10 000 запросов в день на каждую пару приложение - пользователь.
переход: поэтому у нас появилась задача - обойти это ограничение так, чтобы пользователь не ощутил этого на производительности приложения.
(слайд 10) Итак, в чем же главная особенность нашего решения: когда загружаются шоты из любой категории, пользователю необходимо видеть информацию о like или follow для каждого шота. Загружая 10 или 20 шотов нужно проверить информацию о like или follow. При этом нам нельзя блокировать другие запросы пользователя.
В итоге родилась наша очередь запросов с разным приоритетом. Мы разделили запросы по приоритетности (low: check if like, check if follow; high: user info, switch category, like shot)
Когда загружается массив шотов, воркер проходится по нему и отправляет запрос по каждому шоту не чаще одного запроса в минуту. Как только появляется более приоритетный запрос очередь пропускает его вперед очереди и отправляет его, остальные низкоприоритетный запросы ждут своей очереди. Таким образом, пользуясь нашим приложением вы сами можете оценить его производительность.
переход: при разработке нашей очереди мы неоднократно связывались с поддержкой Дрибл
(слайд 11) наше взаимодействие помогло выявить недоработки со стороны backend-а Дрибл. Мы в свою очередь, выяснив логику отправки ошибок и логику обработки ограничений, определились с будущим open source wrapper-ом для Dribbble API.
переход: на сегодняшний день немногие мобильные приложения обходятся без push-уведомлений, я хочу рассказать о нашем концерте push-уведомлений без привлечения серверной стороны.
(слайд 12) для этого нам потребовались условные запросы Дрибл, что это: при запросе с датой в поле хидера last-modified сервер возвращает нам информацию о том изменялся контент с указанной даты или нет. и нам потребовался Background fetch режим работы (появившемся в 7 ios).
Таким образом, приложение просыпается в background fetch режиме -> отправляет условный запрос -> если получает статус 304 -> Засыпает дальше спокойно -> если получает ответ 200 -> Мы показываем локальное push-уведомление.
Конечно, для такого подхода разработчик должен быть уверен в регулярности вызовов background fetch.
Apple в документации говорит о том, что система сама принимает решение когда и как часто включать background fetch. Но в ходе разработки мы отследили, что чем чаще мы передаем константу UIBackgroundFetchResultNewData тем чаще срабатывает background fetch (при том что запрос не должен быть объемным - в нашем случае условный запрос передает только один хидер и отрабатывает быстро). Это по крайней мере единственное успешное решение, которое обеспечивает регулярный вызов background fetch режима.
переход: в итоге нашей разработки мы получили готовый конкурентноспособный продукт и в дополнение реализовали свой концепт
(слайд 13 ) open sorce SDK - на сегодняшний день это самый полный и гибкий warpper для Dribbble API.
переход: следующим шагом для нашей команды стала разработка приложения ShotBucket для Apple Watch.
(слайд 14) Почему именно Shot Bucket мы решили сделать для часов, да потому, что приложение по функционалу очень простое и постраничная навигация очень подходила для ограниченных возможностей фреймворка watchKit, поэтому весной ввиду дефицита девайсов начали разработку на симуляторе, но уже после первого билда для бета-тестирования получили фидбэк с неожиданным результатом и все же приобрели реальный девайс. Поведение реального девайса в некоторых случаях отличалось от документации watchKit.
переход: но обо всем по порядку
(слайд 15) Итак коммуникация между Apple Watch и главным приложением осуществляется на базе bluetooth через watchKit фреймворк, target приложения Apple Watch хранится непосредственно на девайсе часов вместе со статическими ресурсами (картинками и т. д.) и сторибордом (интерфейс), все типы интерфейсов watchKit App статичны и должны быть выполнены только в сториборд до момента запуска приложения. Target WatchKit Extension хранится на телефоне и содержит классы с описанием контроллеров сториборда и другие ресурсы.
переход: весь рабочий код выполняется в главном приложении и без телефона, приложение Apple Watch даже не запустится :)
(слайд 16) немногие разработчики ожидали, что Apple выпустит в разработку настолько сырой продукт
переход: давайте взглянем на процесс запуска приложения Apple Watch
(слайд 17) пользователь запускает приложение на часах -> watcKit находит initial контроллер в сториборде и уведомляет главное приложение через watchKit extension о том, что необходимо создать и загрузить необходимый класс контроллера -> затем происходит инициализация UI элементов и отображение их на экране часов. Хочу отметить роль Initial контроллера: он по сути является делегатом приложения для Apple Watch, первой точкой входа в приложения (из Glance & Notification тоже), назначить initial контроллер можем только из сториборда.
переход: наши дизайнеры нарисовали очень красивый экраны и анимацию для будущего приложения Shot Bucket на Apple Watch
(слайд 18) но возможности интерфейсов фреймворка watchKit остудили их пыл и до 70 процентов дизайна пришлось выкосить.
(слайд 19) вот например стандартные типы интерфейсов: превью и экраны нотификаций короткий и развернутый. первые два не поддерживают интерактивные элементы управления, на них нет вертикального скрола и наложено ограничение на тип шрифта. На таких экранах должно быть минимум информации и полету мысли дизайнеров здесь не место :). Единственный экран поддерживающий кнопки - экран развернутых нотификаций можно использовать (здесь можно немного поработать с дизайном, сделать интерфейс более привлекательным).
переход: в связи с необходимостью разделять код между главным приложением и WatchKit Extension рекомендуется использовать встроенные фреймворки
(слайд 20) apple рекомендует включать во фреймворк общие модели, код отвечающий за авторизацию. И фреймворк необходимо проектировать так, чтобы не обращаться к sharedApplication класса UIApplication, камере и микрофону телефона.
переход: в ходе разработки наша команда исследовала несколько типов коммуникации между WatchKit Extension и главным приложением
(слайд 21) вот они:
- это нативный API watchKit openParentApplication;
- App Groups;
- Darwin-notification
переход: в первую очередь мы начали использовать нативный API, который предлагает документация watchKit
(слайд 22) API позволяет разбудить основное приложение в фоновом режиме, выполнить запрошенные действия и получить ответ вместе с ошибкой. Сложные объекты необходимо сериализовать при передаче. NSError не сериализуется. В процессе использования мы заметили, что каждый успешный вызов reply() задерживает последующий при обновлении UI. А так как в основе нашего приложения лежит галлерея картинок с постраничной навигацией - такая задержка неприемлема для пользователя при быстром свайпе.
переход: еще один тип коммуникации App Group
(слайд 23) два процесса используют общие разделяемые ресурсы App Groups (отдельные песочницы). Запись в контейнер осуществляется путем NSUserDefaults, а доступ через NSFileManager. ограничение: процессы не могут оповещать друг друга об изменении данных !
переход: на данный момент команда пришла к выводу, что наиболее производительным способом коммуникации являются Darwin - нотификации
(слайд 24) в ядре iOS есть процедура, позволяющая одному процессу отправлять сигнал о событии другим процессам; но это только лишь сигнал без какой либо дополнительной информации. На базе таких сигналов был реализован pod ‘MMWormHole’.
переход: подводя небольшой итог хочу сказать, что ограничения в которые поставила Apple разработчиков вызвали у некоторых раздражение, но с другой стороны эти ограничения диктуют правила хорошего тона при работе с микроскопическими экранами.
(слайд 25) И в заключительной части доклада я хочу рассказать особенностях нашего приложения Shot Bucket для Apple Watch
переход:
(слайд 26) Учитывая, что пользователь может изначально запустить приложение либо с часов либо с телефона, мы пришли к решению не пересоздавать источник данных для галлереи для второго процесса, требующего такой источник данных. Т. е. с какого бы устройство не стартовал пользователь приложения последующий процесс подхватит действующий источник данных из делегата приложения.
переход: Таким образом был решен вопрос совместного использования одного ресурса двумя процессами.
Наши опасения того, что поведение watchKit фреймворка отличается от указанного в документации подтвердились после того как у нас появился реальный девайс.
(слайд 27) На схеме указана последовательность вызова делегатов жизненного цикла контроллера watchKit и порядок прихода нотификаций NSExtensionContext. И что же получается если мы говорим о нашей постраничной галлерее шотов: происходит инициализация первой страницы - вызов init и awakeWithContext и соответственно willActivate, но тут же вызывается willActivate и didDeactivate методы для второй страницы в галлерее. Хотя документация нам говорит о том, что «вызов willActivate говорит о появлении данного контроллера на экране часов».
переход: Наша команда определила изменения которые которые были внесены Apple в версию watchKit фреймворка 1.0.1, но причина таких изменений так и осталась неизвестной.
(слайд 28) во многом благодаря этой проблеме мы пришли к следующей реализации нашей галлереи
переход:
(слайд 29) на постраничной навигации нашего приложения есть 10 экранов для галлереи, причем 10-й экран - заглушка c кнопкой «Загрузить еще»; контент загружается в методе awakeWithContext: и когда пользователь проходит по галерее, доходя до 10 экрана, он перегружает контент для всех 10 экранов и возвращается на первый.
Это был очень полезный опыт. Пропускная способность bluetoth ограничена и не всегда стабильна. каждому разработчику стоит дать ответ на 2 главных вопроса:
- что происходит если UI нашего экрана или действие занимает много времени;
- как реагировать приложению apple watch если мы не получили ответ из главного приложения;
переход - вывод: в итоге мы получили готовый, конкурентноспособный продукт, корой вы можете найти в Appstore; и сейчас мы продолжаем работу над его оптимизацией и совершенствованием.
На экране QR код по которому вы можете его скачать.
На этом все, спасибо за внимание, если есть какие-нибудь вопросы - пожалуйста задавайте.