Приложение для Apple Watch за час

Apple Watch вот-вот появится у миллионов людей по всему миру. Разработчики готовятся к этому знаменательному событию, адаптируют свои приложения, сталкиваются с проблемами, решают их.

Мы с Ильёй не планировали делать ничего для Ангстрема. Вместо этого сосредоточились на стабилизации того, что есть, написании тестов, и продумывании некоторых моментов, которые оказались менее крутыми, чем мы думали изначально. Но Ангстрем — это «проект выходного дня», на основной работе потребовалось изучить на примерах, что же может Apple Watch. Я решил, что стоит попробовать сделать это на примере Ангстрема.

Задача была — разобраться с базовыми вещами:

  • настройка проекта,
  • взаимодействие приложений на часах и на Айфоне,
  • технические особенности,
  • ограничения,
  • возможно, особенности отправки приложения в стор.

Ангстрем подошел идеально из-за того, что в нём поддерживается диктовка. Собственно, это единственный способ ввода текста на Apple Watch, помимо закодированных заранее строк. Осталось только прикрутить её к часам.

Создание и настройка проекта

В теории всё просто. Добавляем таргет WatchKitApp в проект, их появляется два (WatchKit Extension, WatchKit Application), и должно работать. На деле оказалось, что Xcode не успевает за новыми железками и бета-версия 6.3, которую я тогда использовал, не умеет собирать приложения WatchKit, пришлось немного погуглить, поправить руками файл проекта.

В дальнейшем, разбираясь со сборкой проекта, я нашел еще несколько важных моментов:

  • у вас в проекте будет три info.plist'а. Для Айфон-приложения, для вотч-екстеншна и вотч-приложения. У них должны быть одинаковые версии (обе, как CFBundleVersion так и CFBundleShortVersionString).
  • для вотч-екстеншна и вотч-приложения нужно создать пустые entitlements, так как они сейчас не поддерживают примерно ничего (ни бета-тестирование, ни группы, например). Изначально визард что-то такое сделал, но я подумал, что оно лишнее и удалил. Зря, пришлось восстанавливать.
  • для вотч-екстеншна и вотч-приложения нужно сделать свои провижн-профайлы, как и для других екстеншнов. Очень неудобно получается, но что делать.
  • таргет-версия для приложения и для екстеншнов может различаться (как и для других екстеншнов), например, для приложения 7.0, для вотча должна быть 8.2.

Разобравшись с проектом и запустив приложение, получив черный экран, я пошел разбираться с интерфейсом.

Интерфейс приложений для Apple Watch

 
 

Разрабатывается интерфейс в том же Xcode при помощи привычных сторибоардов. Возможностей, правда, сильно меньше и компоненты совершенно другие, лейаут другой. Я делал только экран самого приложения, глэнсы и нотификации не трогал.

Вот, например, как выглядят настройки компонента «Image», работающего с картинками и покадровой анимацией.

 
 

Сверху все просто, а разделы Position и Size — это как раз новый лейаут. Position определяет, куда этот компонент будет «стремиться». Если все компоненты по-вертикали позиционируются сверху, то получится вот так (они накидываются в скролл-вью сверху вниз):

 
 

А если в поле «позиция» прописать «Bottom», то вот так:

 
 

Можно, например, комбинировать, что-то прибить к верху, что-то к низу.

 
 

Поле «Размер» — отвечает за размер в соответствующем направлении. Есть три типа:

  • чтобы содержимое поместилось (вполне очевидно)
  • относительно контейнера (число рядом задает долю размера контейнера, которую нужно занять)
  • фиксированный размер (в поинтах)

Например, чтобы расставить вьюшки наверх и вниз, чтобы верхняя занимала 0.2 высоты, а нижняя — 0.7, а между ними была дырка, нужно выставить значения как-то так:

Adjustments позволяет «дотюнить» позицию до нужной в случае необходимости.

Как только понимаешь значения каждого из этих параметров, остальное становится достаточно просто. Сложные интерфейсы делаются тут при помощи вложения контейнеров в другие контейнеры.

Остальные моменты проще посмотреть в официальном документе.

Код

Почему вообще Apple Watch требует два (а с основным — три) таргета? Всё просто:

  • WatchKit App запускается на часах. Содержит интерфейс, ресурсы для его отображения. Но не умеет ничего кроме получения действий от пользователя и отображения простых лейаутов.
  • WatchKit Extension работает на Айфоне и отвечает за действия интерфейса на часах. Сюда приходят все события от часов, отсюда в интерфейс уходят команды на изменение интерфейса.
  • iPhone App может вызываться из WatchKit Extension и вынужден выполнять всю основную работу.

Для меня было три момента, с которыми нужно было разобраться: - как получить от пользователя ввод (в какой-нибудь форме) и - как сконвертировать ввод пользователя - как вернуть и отобразить результат обратно

Первый пункт Эпл решила за меня. Ввод на часах доступен только при помощи диктовки или заранее подготовленных строк. Заранее ничего подготовить я не могу, поэтому оставался единственный вариант, диктовка.

Расчеты удалось (в отличие от тудей-екстеншна) провести в основном приложении, которое всё уже умеет. Делается это при помощи метода openParentApplication:reply: который как принимает NSDictionary значений, так и возвращает. Единственный важный момент заключается в том, что все значения должны нормально сериализовываться. Строки, числа, NSData — будут переданы отлично. Кастомные классы — только после соответствующей подготовки.

С возвращением (помимо того, что NSAttributedString напрямую передавать нельзя) возникли проблемы. Хотелось сделать красиво, с правильными шрифтами, отступами. Илья нарисовал красивый дизайн... В результате я отказался от вывода результата при помощи NSAttributedString, как хотел изначально. Это бы потребовало добавления нескольких шрифтов на часы и сложной многопроходной логики редеринга результата. Более простым решением оказалось создавать картинки на Айфоне, которые уже передавать.

Это решение оказалось удобным ещё и для тестирования. Я написал небольшую подпрограмму, которая выводит много разных результатов в картинки, чтобы мы с Ильёй смогли проверить красоту. Проверяли, отсматривая вот такие простыни картинок (2 языка, 2 размера экрана часов, больше тысячи картинок для каждого).

Всё остальное оказалось делом техники. Конечно же, пришлось потратить несколько часов на то, чтобы улучшить само распознавание наговоренного человеком текста, но в остальном, приложение не потребовало от меня больших затрат. Основа была сделана, как я и написал в заголовке, за час. Я написал несколько десятков строчек кода, а остальное время разбирался с нюансами, API, HIG и тому подобным.

Отладка приложения в симуляторе

Отлаживать на устройстве не получилось по очевидной причине, нет устройства. Симулятор тоже работает странно (нужно выбрать в «дополнительных экранах» нужный размер экрана вотча, он появится вторым окном в симуляторе). Нет ни хоум-скрина, не работает диктовка (пришлось исхитряться), брякпоинты работают через раз (таргетов три, нужно понимать, к какому процессу подключаться для отладки той или иной фичи) и так далее.

Поэтому я всю основную работу делал в приложении. Сэмулировал вызов нужного метода, получал и проверял результат. Поскольку интерфейс с часами очень простой (туда строка, размер экрана, обратно — картинка), то и сделать это оказалось достаточно просто.

Сборка приложения

Кроме создания приложения нужно его выложить. За сборку всех приложений у меня отвечает моя собственная система, Bilt, которая умеет готовить приложение для AdHoc или AppStore распространения, выкачивать провижн-профайлы, встраивать Fabric, прописывать правильные версии и дополнительные параметры, упрощающие в дальнейшем анализ ошибок, и вкачивать все это либо в Fabric, либо в iTunesConnect.

Эта система никак не хотела собирать и вкачивать приложение. Добраться до источника проблемы получилось на закрытых Apple-форумах, где выяснилось, что xcodebuild, которым производится сборка, не умеет делать ipa-файлы с поддержкой Apple Watch. И нужно либо руками прописывать там хитрую структуру каталогов, либо не париться и воспользоваться Xcode, в котором кнопка «отправить в AppStore» работает. Поэтому вся подготовительная работа у меня делается Bilt'ом, а потом полученный archive-файл я открываю в Xcode и завершаю отправление приложения в стор. Надеюсь, что xcodebuild допилят и все станет хорошо.

Проблемы со шрифтами

Шрифты — то, с чем пришлось повозиться, потому что хотелось и красиво и «как принято в Apple Watch», то есть, новой гарнитурой, Сан-Франциско.

Выяснилось, что в Сан-Франциско нет русских букв, вместо них подставляется Гельветика. В экранном варианте шрифта нет также и курсива. С некоторыми символами происходят странные трансформации. Так, µ, к примеру, становится курсивной.

Пришлось придумать десяток хитростей, чтобы нивелировать все эти мелочи и стало хорошо.

Результат

Получилась магия. Я, тестируя обычной диктовкой на Айфоне, радуюсь, как ребенок. Говоришь ему «New York», он показывает текущее время в Нью-Йорке.

У меня английский UI на Айфоне, поэтому доступна только ASCII-клавиатура и только английская диктовка. Мне так приятнее. Отстутствие лишних клавиатур также сильно ускоряет ввод, не нужно их постоянно переключать. Запустил, ввел пару букв латиницей и получил результат.

Или говоришь ему «6 feet 2 inches», чтобы узнать, какой рост у Стива Джобса в понятных величинах, а Ангстрем такой: «6,1666666 ft = 1,88 m». Или «UK Pound rate», или «how many cups in a liter», или «twelve hundred in Tokyo», или «3 ångström». И оно понимает!

Магия!