- PVSM.RU - https://www.pvsm.ru -
Привет! Меня зовут Денис Попов, и я iOS-разработчик в QIC digital hub. В этой статье я расскажу о кодогенерации в мобильной разработке: кто действительно нуждается в ней, как она применяется на практике и какую ценность можно извлечь из этого процесса.
Мой путь в мире мобильной разработки начался в 2018 году. За это время я работал в нескольких компаниях и сталкивался с различными задачами, связанными с кодогенерацией. В каждой из этих организаций мы искали индивидуальные решения и использовали разные инструменты. В QIC я также обнаружил несколько интересных примеров использования кодогенерации, которыми с удовольствием поделюсь в этой статье.
Для нас, как для разработчиков, кодогенерация предоставляет множество преимуществ: мы пишем меньше кода, уменьшаем вероятность ошибок, а сам код становится более консистентным.
Кодогенерация помогает решать систематически возникающие проблемы в процессе разработки и вопросы, требующие времени и взаимодействия с коллегами. Важно отметить, что кодогенерация не является универсальным решением, и в вашем проекте вы можете столкнуться с подводными камнями, которые нужно будет решать, ища обходные пути.

Я разбил жизненный цикл разработки функциональности на три основных блока: получение данных от сервера, их обработка и подготовка к отображению. Следующим шагом является рендеринг макетов, которые нам подготовили дизайнеры, а в конце пути мы должны проанализировать, какой профит мы получили от этой функциональности — то есть отправить аналитику.
Даже в самом простом случае, например, при обработке ответа от сервера, реализации на платформах iOS и Android могут отличаться. Кто-то декодирует поля по-разному, и из-за этого изменяется реализация функции. Для решения даже таких минимальных проблем мы используем Swagger Codegen. Он фактически генерирует модели, описанные в спецификации OpenAPI. Это публичный инструмент, который вы также можете использовать в своем проекте. Swagger Codegen генерирует как клиентские SDK, так и документацию, и дает возможность использовать свои шаблоны или дефолтные.
Я постарался описать в четырех шагах, как этот процесс работает у нас:

При проработке функции мы описываем спецификацию конкретной модели, после чего выбираем нужный генератор — например, генератор под iOS. При необходимости на третьем шаге можно добавить кастомные параметры, которые будет использовать генератор. В конце концов, мы получаем сгенерированный код, который можем использовать в разработке. Таким образом мы, как мобильные разработчики, можем сосредоточиться на написании логики, избегая создания дополнительного boilerplate-кода.
Пример описания спецификации:

Для примера я взял простую спецификацию, касающуюся AppConfigResponse. На слайде вы можете увидеть, что есть поле response, которое является обязательным и ссылается на созданную схему. Это позволяет нам переиспользовать уже существующие модели, что упрощает описание спецификаций.
Результат на картинке:

Слева вы видите AppConfig Response для iOS, справа — для Android. Это одни и те же поля, и нам не нужно ничего дополнительно делать. Казалось бы, все отлично: мы написали спецификацию, не внося изменений в код. Аналитики, в свою очередь, описали схемы, которые мы можем использовать.
Однако вскоре мы столкнулись с проблемой. У нас была задача разработать новую функцию, для чего мы создали отдельный endpoint и надеялись получить ответ, содержащий массив объектов, где мы передаем type и в зависимости от него декодируем объект.
Ожидания:

Проведя некоторое время на Stack Overflow и погуглив, мы написали спецификацию и были довольны результатом. Но генератор имел свое мнение и сгенерировал что-то, что не собиралось. Реальность:

Как это исправить, было не совсем ясно, а у нас, как всегда, были сжатые дедлайны и недостаточно времени на исследование.
В итоге мы нашли альтернативное решение: начали передавать опциональные параметры. К счастью, поскольку функция была небольшой и параметров не так много, это дало возможность передавать type и необходимые для каждого объекта опциональные параметры.

На данный момент мы уже обработали модели и ответ сервера и готовы к отображению данных. В этом блоке, посвященном рендерингу, я хотел бы обсудить один важный аспект. Возможно, в небольших проектах его не так очевидно заметить, но с ростом проектов вы, скорее всего, услышите термин «дизайн-система». Каждая компания и проект имеют свои уникальные требования и пожелания относительно дизайн-системы. В нашем проекте она уже внедрена. Для примера я хочу рассмотреть работу с цветами.
Мы генерируем цвета из Figma, для чего написали пользовательский инструмент, который извлекает цвета, описанные нашими дизайнерами, и создает расширение с этими цветами.

У нас есть YAML-конфигурационный файл, из которого мы берем путь для обращения к API Figma. Через API мы получаем все стили; в моем примере — цвета. Это массив цветов, которые используют дизайнеры при создании макетов. Это удобно как для дизайнеров в Figma, так и для нас при обработке данных. Получив всю информацию, мы выбираем нужный шаблон для генерации кода в зависимости от платформы — iOS или Android. В итоге мы получаем строку кода, которую вставляем в нужный файл проекта.
Теперь давайте посмотрим на конкретные примеры, так это будет более наглядно.

На картинке вы видите нашу таблицу в Figma, составленную дизайнерами, где указаны необходимые цвета и их применение. Важно понимать, что у каждого цвета есть токен с именем и его значением в формате HEX. Для удобства работы в Figma мы добавили визуальное отображение, показывающее, как цвет выглядит на самом деле. Обратите внимание, что в таблице справа есть колонки, которые еще не заполнены. Эти колонки предназначены для темной темы. У нас в приложении ее сейчас нет, но мы предусмотрели возможность ее добавления в будущем. Заполнив такую таблицу, мы сможем быстро реализовать темную тему с минимальными затратами. Однако стоит отметить, что здесь есть свои подводные камни, о которых я расскажу позже.
В результате мы извлекли цвета из Figma, и для iOS я покажу простой шаблон, который мы используем для генерации статических переменных (static vars) в расширении.

Мы просто сохраняем RGB-значения.

На двух скриншотах слева представлен пример для iOS, а справа — для Android. Как видно, сгенерированные цвета совпадают, что приятно.
Хочу также поделиться интересной историей, которая произошла со мной в середине лета. Мне была поставлена задача перекрасить все приложение. Все, что мне нужно было сделать, — это в командной строке ввести команду для повторной генерации цветов из дизайн-системы, и значения в файле автоматически обновились. Это безусловно удобно. Однако, к сожалению, не все токены из Figma были использованы в проекте или изменились названия. Поэтому мне, как разработчику, пришло время проверить код, исправить несколько ошибок в нейминге и уточнить у дизайнеров, использованы ли правильные цвета для токенов. Тем не менее инструмент значительно сократил мое время на обработку, и я смог быстро завершить задачу. Я считаю это достаточно успешным опытом.
В конце этого блока я хотел бы поделиться всеми зависимостями, которые использует наш инструмент для генерации кода. Это на самом деле не так сложно.
Двигаемся дальше. После того как мы внедрили функцию, необходимо покрыть ее аналитикой. Я думаю, многие сталкивались с ситуациями, когда данные в аналитике могут расходиться, например, когда на iOS что-то не отправляется, а на Android, наоборот, отправляется слишком много параметров. Чтобы избежать этих проблем, мы используем KMM.
Важно отметить, что это не генерация кода, как я описывал ранее, а компиляция Kotlin-кода из основного модуля в нативный для iOS. В результате мы получаем framework-файл, который используем как библиотеку в iOS. Я немного отклонился от темы, но хотел акцентировать внимание на том, что у нас есть четкий процесс версионирования с единым источником правды.
Как это выглядит на практике? Мы описываем события аналитики для каждого экрана в проекте. Обычно они могут быть как кастомными, так и статичными. На каждом экране мы точно знаем, какие события могут быть отправлены.


Для iOS и Android предусмотрен единый интерфейс, что исключает возможность отправки лишних данных или пропуска необходимых.

На этом примере показан процесс на iOS. Нам достаточно вызвать событие с нужным экраном, и это очень удобно.
Теперь расскажу о процессе покрытия аналитики. Обычно создаются три тикета: для iOS, для Android и один для обновления KMM. Прелесть в том, что весь процесс может быть выполнен одним разработчиком, который затем может передать эстафету команде.
На первом шаге мы создаем pull request с описанием новых событий. Возможно, потребуется пообщаться с аналитиками для оптимизации событий и их описания. При создании pull request срабатывает триггер, который собирает предпросмотр сборки. Пока pull request открыт, можно оставлять комментарии, и мы можем проверить, правильно ли собирается код и всё ли указано верно перед внесением изменений в основную ветку.
Отдельно выделю, что при каждом обновлении pull request пересобирается предпросмотр, что позволяет нам проверить и избежать попадания в основную ветку неподходящего кода. На последнем шаге мы получаем новую версию релиза, и разработчики могут обновить версии для новых функций.
Кодогенерация — замечательный инструмент, но его нужно использовать обдуманно. Злоупотребление кодогенерацией может ограничить возможности кастомизации, однако это, в свою очередь, заставляет команды работать эффективнее. В QIC мы постоянно улучшаем процессы и стремимся не создавать костылей. Если что-то идет не так, мы стараемся находить элегантные решения. А как вы используете кодген?
Автор: vegopunk
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/408074
Ссылки в тексте:
[1] Источник: https://habr.com/ru/articles/874606/?utm_source=habrahabr&utm_medium=rss&utm_campaign=874606
Нажмите здесь для печати.