![](https://habrastorage.org/getpro/habr/upload_files/6e1/745/aba/6e1745aba3f688264f313eaffdc2c423.jpg)
Недавно мы запустили офлайн карты для Apple Watch. Проект занял более 2 лет и потребовал огромного внимания со стороны нашей небольшой команды энтузиастов. Мы столкнулись с таким количеством продуктовых и технических сложностей, что просто не может не поделиться этим с вами. Вот наша история.
Идея
Идея офлайн карт далеко не нова. В нашем мире существует множество мест, где беспроводное соединение с интернетом все еще недоступно или слишком нестабильно. Там людям нужны карты, которые смогут работать на их устройствах без интернета.
Существует множество хороших приложений для мобильных устройств, предлагающих офлайн карты, но не в случае Apple Watch, для которых такие карты были бы тоже полезны. Если у вас с собой iPhone, большую часть времени ваши часы полагаются на интернет-соединение через него. Однако, если ваш iPhone остался дома, только eSim может решить эту проблему, но далеко не у всех есть eSim.
Офлайн карты, способные работать на часах без соединения с интернетом и iPhone кажутся интересным, сложным и полезным проектом. Поэтому два года назад пара друзей решили его реализовать. Так началась история Goose Maps.
Аудитория
Как и в случае с любым новым проектом, самый первый вопрос был о том, кто наши пользователи и сколько их.
Для того, чтобы это выяснить, мы начали с определения технических ограничений. Мы купили несколько разных моделей и после первоначального тестирования прототипа выяснили, что Apple Watch 5 были самой ранней моделью, обеспечивающей достаточно хорошее взаимодействие с пользователем (UX) в рамках наших целей. Мы все еще могли работать на более ранних моделях, но с некоторыми проблемами, такими как более медленный рендеринг или скорость передачи файлов (нам требовался довольно большой объем данных для работы в автономном режиме).
В том году Apple Watch 6 были самой старшей версией (при наличии в активной продаже гораздо более ограниченной 3-ей модели). Все, что нам было известно, это количество активных устройств (около 100 миллионов), но без какого-либо распределения по моделям. Мы сделали приблизительные расчеты и оценили совокупный доступный рынок в десятки миллионов устройств.
Наличие большого количества потенциальных пользователей было хорошим началом, но еще важно было понять, как Apple Watch используются в реальности. После некоторых дополнительных исследований стало ясно, что есть большая вероятность того, что многие люди не используют сторонние приложения, а в основном полагаются на встроенное программное обеспечение. Хотя мы видели, что существуют популярные сторонние приложениями для Apple Watch, эта теория представляла большой риск для всего предприятия.
Нужно было сделать понятное бизнес-предположение. И мы сделали следующее: на рынке не было простых в использовании автономных офлайн карт, в том время как Apple все более активно продвигал независимость часов [от iPhone], фокусируясь на использования часов для здорового образа жизни, спорта и других активностей такого рода. Мы договорились, что наши карты должны быть максимально автономными, простыми в использовании и поддерживающими людей с активным образом жизни, начиная с катания на велосипеде в парке, заканчивая походами в горы. Так, мы рассчитывали, что будем преследовать ту же цель, что и Apple.
Принимая это во внимание, мы сформировали следующий набор функциональных требований.
Функциональные требования
Офлайн. Приложение должно скачивать карту один раз и использовать ее потом неограниченно долго. Карта должна иметь малый размер для того, чтобы быстро скачиваться и не занимать слишком много места на диске.
Автономность. Пользователь может оставлять свой телефон дома, и это нормально. Вся работа (навигация, рендеринг, сетевое взаимодействие) должно происходить на Apple Watch и обеспечивать приятный UX. Наличие iPhone может улучшать UX, но не должно блокировать использование приложения.
Навигация. Наше приложение должно уметь строить эффективные пешеходные и велосипедные маршруты с пошаговой навигацией.
Поиск. Мы должны предоставить поисковую систему для полезных объектов на карте.
Понятные и контрастные стили карт. Карта должна легко считываться в течение короткого времени, когда вы поднимаете запястье. Даже в солнечную погоду.
Лаконичный и простой пользовательский интерфейс. Маленькие экраны и короткие пользовательские сессии не должны ухудшать UX. Пользовательский интерфейс должен быть лаконичным, умным и обеспечивать легкий доступ ко всем функциям.
Данные
Мы используем данные OpenStreetMap. OSM использует краудсорсинг, и качество региона зависит в первую очередь от размера местного сообщества. Некоторые регионы (например, части Европы) на самом деле очень хороши и могут конкурировать или даже выигрывать у коммерческих карт. В целом, это самый известный источник бесплатных и открытых картографических данных для всего мира, поэтому у нас не было выбора.
Маленькие экраны часов поставили перед нами дополнительные задачи. Существует так много объектов, которые вы можете одновременно отобразить на экране, что вам нужно либо обеспечить достаточное масштабирование карты, чтобы в конечном итоге показать каждый объект, либо отфильтровать часть из них. Нам пришлось фильтровать данные и отображать наиболее важные объекты для обеспечения приятного UX.
После этого мы столкнулись с первой серьезной проблемой. Несмотря на то, что у нас были очень хорошие алгоритмы сжатия, регионы на нашей карте все еще были слишком большими, чтобы их можно было достаточно быстро загрузить на Apple Watch. Эксперименты показали, что скорость загрузки снижается нелинейно с ростом скачиваемого объема данных на часах. Кроме того, эта проблема усугублялась, если экран был выключен, что происходит на Apple Watch постоянно. В этом состоянии приложение может быть легко уничтожено WatchOS, и некоторые большие файлы никогда не будут загружены. Это критично для приложения, которому они нужны для работы.
Наши тесты и обсуждения привели нас к следующему выводу: время загрузки карты не должно превышать время, в течение которого мы можем заинтересовать пользователя, и у нас было в среднем 5–7 секунд. В конечном счете, мы остановились на 5 Мб для первого скачиваемого блока данных и 20–25 Мб в качестве максимального размера передаваемых данных для карты.
Разбить карту мира на части такого размера оказалось непросто. Мы использовали двунаправленный подход — разделение данных как по вертикали, так и по горизонтали. Вертикальное разбиение делило данные карты на слои и позволяло достаточно быстро загружать и показывать наиболее важные их них, в то время как менее важные еще находились в процессе [скачивания]. Ландшафт, дороги, достопримечательности, данные для построения маршрутов и поиска, изолинии — все было разделено на слои. Мы определили два типа слоев: обязательные и опциональные. Обязательные слои необходимо было загрузить до того, как пользователь сможет взаимодействовать с картой или использовать некоторые базовые функции, такие как поиск. Опциональные можно было загрузить позже, хотя некоторые дополнительные функции были недоступны без них. После того, как карта была полностью загружена, она оставалась на устройстве для использования в автономном режиме.
Горизонтальное разделение позволило нам контролировать полный размер карты. Там, где это было возможно, мы разделили карту с учетом административных границ, но во многих случаях нам пришлось увеличить детализацию. Это заняло много времени, но в итоге нам удалось это сделать.
![Рис.1. Пример горизонтального разбиения карты. Рис.1. Пример горизонтального разбиения карты.](https://habrastorage.org/getpro/habr/upload_files/cae/780/a6b/cae780a6b2e9a81d2e76b8976928c3a7.png)
Рендеринг
Рендеринг — важная часть любой интерактивной карты. Изначально мы сомневались, что Apple Watch смогут отобразить большое количество треугольников, из которых состоит карта. Однако чипы Apple для часов продемонстрировали на удивление хорошую производительность. Основная проблема заключалась в том, что WatchOS не предоставляет прямого доступа к Metal API. Единственный способ отрендерить что-то в Apple Watch — использовать фреймворк SceneKit.
К сожалению, SceneKit предназначен в большей степени для видеоигр, а не для интерактивных карт. В частности, карты не требуют сложных деревьев сцен, но требуют постоянных операций с памятью. В SceneKit нам пришлось пересоздавать тайлы карты вместо того, чтобы обновлять текущие в памяти. Кроме того, SceneKit не поддерживает пользовательские шейдеры для WatchOS, поэтому нам пришлось использовать стандартные.
Чтобы решить все эти проблемы, мы создали движок рендеринга под названием Atlas. Atlas состоял из двух основных частей: многопоточного тайлового сервера, написанного на C++, и легковесного фронтенда на Swift с использованием SceneKit.
![Рис.2. Схема графического движка Atlas. Рис.2. Схема графического движка Atlas.](https://habrastorage.org/getpro/habr/upload_files/9fa/c06/a94/9fac06a94448706a230bcdf65eaffc7e.png)
Тайловый сервер подготавливает векторные тайлы, содержащие однородную геометрию для любых картографических данных, и отправляет их во фронтенд. Фронтенд создает новые примитивы SceneKit и управляет существующими. Кроме того, фронтенд управляет камерой, анимациями и кэшированием. В совокупности с правильно подготовленными стилями мы добились приятно выглядящих карт, которые эффективно работают на целевом устройстве.
Стили карт
Apple Watch потребовали от нас особого внимания к стилям карты. На то было несколько причин:
Короткая пользовательская сессия. Рука устает, если долго смотреть на часы на запястье. Поэтому карта должна легко считываться.
Очень маленький экран. Нет возможности показать все типы объектов на карте, так как это замедляет считывание.
Основной цвет пользовательских интерфейсов Apple Watch — черный. Это обусловлено энергоэффективностью.
Ограничения SceneKit. Отсутствие программируемых шейдеров ограничивает нас в способах визуализации.
Таким образом, мы начали с темного стиля карты. Мы старались подобрать цвета, которые хорошо читаются днем, видны людям с ограничениями в зрении и в некоторой степени соотносятся с нашим брендом. Иногда эти требования противоречили друг другу, но после нескольких итераций мы добились своего.
![Рис.3. Цветовая схема для темной темы в нашем инструменте для дизайна. Рис.3. Цветовая схема для темной темы в нашем инструменте для дизайна.](https://habrastorage.org/getpro/habr/upload_files/578/15b/991/57815b9912a68d1d83d63a7e26da6bbb.png)
Однако в процессе бета-тестирования мы обнаружили, что некоторые пользователи сильно привыкли к «традиционным» цветовым схемам, поэтому в нашем приложении появился и светлый стиль. После, мы добавили специальный стиль для активного отдыха в горах (хайкинга).
![Рис.4. Стили карт в Goose Maps. Рис.4. Стили карт в Goose Maps.](https://habrastorage.org/getpro/habr/upload_files/b33/06b/4d0/b3306b4d0572b020bd358b58f9a585fd.png)
Построение маршрутов и поиск
Построение маршрутов и поиск важны для любой интерактивной карты и зависят друг от друга. Без поиска вы можете строить маршруты только к известным местам (или найденным на карте вручную). Вы не узнаете, как добраться до места, если у вас есть только поиск, но нет построения маршрутов. Мы реализовали маршрутизацию для пеших и велосипедных прогулок, главных популярных видов активного отдыха.
С поиском было немного сложнее. В Apple Watch нет удобного способа для набора текста. Один из вариантов — интегрировать Siri, но мы отказались от него, так как это работало для произвольных языков только при наличии интернет-соединения. Другим вариантом было использование экранной клавиатуры, появившейся в последних моделях. Проблема заключалась в том, что экранная клавиатура поддерживала только английский язык и, по нашему мнению, все еще была неудобна для пользователя. Поэтому мы определили наиболее полезные категории поиска для основных вариантов использования нашего приложения и реализовали поиск по этим категориям. Этот компромисс помог нам упростить интерфейс и обеспечить хороший UX.
Пользовательский интерфейс
Наши карты использует фреймворк SwiftUI. Этот достаточно новый фреймворк, ориентированный на быстрое прототипирование динамических интерфейсов. Обычно для создания сложных интерфейсов с использованием SwiftUI требуется очень мало кода, но если вам нужно что-то нестандартное, это может быть сложно реализовать. Однако, принимая во внимание ограничения пользовательских интерфейсов в Apple Watch, SwiftUI идеально подходит для создания подобных интерфейсов.
В отличие от Apple Maps для часов, мы сделали карту главным экраном приложения. Две постоянные отображаемые кнопки управляют геопозиционированием и поиском. Одна дополнительная кнопка может появляться в зависимости от контекста. Клик по объекту на карте открывает страницу описания, где пользователь может проложить маршрут к этому месту или поставить метку для быстрого доступа позже (метки можно также добавить при помощи долгого клика).
Скроллинг карты работает как обычно с помощью жестов перетаскивания, масштабирование работает при помощи бокового колеса.
![Рис.5. Поиск Рис.5. Поиск](https://habrastorage.org/getpro/habr/upload_files/688/4f1/e9b/6884f1e9bdc6ca30a53fe6f92febe5d1.png)
![Рис.6. Построение маршрута. Рис.6. Построение маршрута.](https://habrastorage.org/getpro/habr/upload_files/28d/842/de9/28d842de96952d2d391250b6b4f6baf5.png)
![Рис.7. Метки на карте Рис.7. Метки на карте](https://habrastorage.org/getpro/habr/upload_files/d83/d80/203/d83d8020375ba76133c1967a48a3a0bb.png)
Приложение-компаньон
Изначально наше приложение не содержало приложения-компаньона на iPhone в принципе, однако после бета-тестирования мы пересмотрели это.
Самая серьезная проблема, которую мы обнаружили во время бета-тестирования, заключалась в том, что покупки на Apple Watch не работали должным образом из-за какой-то внутренней проблемы. Мы долго боролись с этим и сообщили о проблеме в Apple, но никакой помощи не получили. Чтобы отладить эту проблему, мы даже создали небольшое приложение, содержащее программный код покупок, который мы протестировали в продакшне. Наконец, мы сдались и переместили код покупок в приложение-компаньон на iPhone, где он работал, как и ожидалось.
Это было хорошим решением, ведь позже мы узнали, что никто, включая Apple, не может таргетировать рекламу на пользователей с устройствами Apple Watch. Это было угрозой для наших будущих маркетинговых усилий.
Итак, мы добавили на iPhone приложение-компаньон для приложения на Apple Watch. Обычно все происходит ровно наоборот.
Запуск
Наконец, 7 сентября 2022 года, после очень долгого периода проб и ошибок, мы выпустили приложение Goose Maps for Watch. Для нас это был конец одного эпического путешествия и начало другого. Теперь мы сосредоточимся на бизнес-деятельности и посмотрим, окупятся ли наши ставки.
Всех, у кого есть Apple Watch, мы приглашаем попробовать наше приложение в рамках бесплатной 7-дневной пробной версии. Мы будем благодарны за любые отзывы о нашем продукте. Это поможет нам быстрее двигаться вперед.
До встречи в Goose Maps for Watch!