Кодогенерация: кому нужна и как настроить

Привет! Меня зовут Денис Попов, и я iOS-разработчик в QIC digital hub. В этой статье я расскажу о кодогенерации в мобильной разработке: кто действительно нуждается в ней, как она применяется на практике и какую ценность можно извлечь из этого процесса.

Мой путь в мире мобильной разработки начался в 2018 году. За это время я работал в нескольких компаниях и сталкивался с различными задачами, связанными с кодогенерацией. В каждой из этих организаций мы искали индивидуальные решения и использовали разные инструменты. В QIC я также обнаружил несколько интересных примеров использования кодогенерации, которыми с удовольствием поделюсь в этой статье.

Преимущества кодогенерации

Для нас, как для разработчиков, кодогенерация предоставляет множество преимуществ: мы пишем меньше кода, уменьшаем вероятность ошибок, а сам код становится более консистентным.

Проблемы, которые решает кодогенерация

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

cfbd4c5a425abd63b6e8a712d0e9c21c.png

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

Первый шаг: получение данных

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

Я постарался описать в четырех шагах, как этот процесс работает у нас:  

2651a79134561886804573733d6d0493.png

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

Пример описания спецификации:

e5a0ac65968c3df57a382689286eb82f.png

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

Результат на картинке:

4e54a6b5bba702dce73c193457bc6778.png

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

Однако вскоре мы столкнулись с проблемой. У нас была задача разработать новую функцию, для чего мы создали отдельный endpoint и надеялись получить ответ, содержащий массив объектов, где мы передаем type и в зависимости от него декодируем объект.

Ожидания:

4ce5b077d112211edaa2d58886ace498.png

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

c007a9bd8cb43c98e9e744ca9af2e017.png

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

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

946baf81559dc62e99f9d9199233024f.png

Второй шаг: рендеринг

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

Мы генерируем цвета из Figma, для чего написали пользовательский инструмент, который извлекает цвета, описанные нашими дизайнерами, и создает расширение с этими цветами.

bd8db1e5b2bee5f9c45acb5812840557.png

Что под капотом

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

Теперь давайте посмотрим на конкретные примеры, так это будет более наглядно.

e909ad0c935d5b21432ec8e19773d0a8.png

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

В результате мы извлекли цвета из Figma, и для iOS я покажу простой шаблон, который мы используем для генерации статических переменных (static vars) в расширении.

69fde2cf52de3e9b45cc60c79c3ab805.png

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

834d223d2f45a0ef8343c2fad255cdd8.png

На двух скриншотах слева представлен пример для iOS, а справа — для Android. Как видно, сгенерированные цвета совпадают, что приятно.

Хочу также поделиться интересной историей, которая произошла со мной в середине лета. Мне была поставлена задача перекрасить все приложение. Все, что мне нужно было сделать, — это в командной строке ввести команду для повторной генерации цветов из дизайн-системы, и значения в файле автоматически обновились. Это безусловно удобно. Однако, к сожалению, не все токены из Figma были использованы в проекте или изменились названия. Поэтому мне, как разработчику, пришло время проверить код, исправить несколько ошибок в нейминге и уточнить у дизайнеров, использованы ли правильные цвета для токенов. Тем не менее инструмент значительно сократил мое время на обработку, и я смог быстро завершить задачу. Я считаю это достаточно успешным опытом.

В конце этого блока я хотел бы поделиться всеми зависимостями, которые использует наш инструмент для генерации кода. Это на самом деле не так сложно.

Аналитика и KMM

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

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

Процесс аналитики

Как это выглядит на практике? Мы описываем события аналитики для каждого экрана в проекте. Обычно они могут быть как кастомными, так и статичными. На каждом экране мы точно знаем, какие события могут быть отправлены.

0735c52bc75f6837b60951331656e03f.pngcc745790530bd48ae871bd7ebcf9edd1.png

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

ba547bd44857b905c6e86e29b2a36321.png

На этом примере показан процесс на iOS. Нам достаточно вызвать событие с нужным экраном, и это очень удобно.

Теперь расскажу о процессе покрытия аналитики. Обычно создаются три тикета: для iOS, для Android и один для обновления KMM. Прелесть в том, что весь процесс может быть выполнен одним разработчиком, который затем может передать эстафету команде.

На первом шаге мы создаем pull request с описанием новых событий. Возможно, потребуется пообщаться с аналитиками для оптимизации событий и их описания. При создании pull request срабатывает триггер, который собирает предпросмотр сборки. Пока pull request открыт, можно оставлять комментарии, и мы можем проверить, правильно ли собирается код и всё ли указано верно перед внесением изменений в основную ветку.

Отдельно выделю, что при каждом обновлении pull request пересобирается предпросмотр, что позволяет нам проверить и избежать попадания в основную ветку неподходящего кода. На последнем шаге мы получаем новую версию релиза, и разработчики могут обновить версии для новых функций.

Заключение

Кодогенерация — замечательный инструмент, но его нужно использовать обдуманно. Злоупотребление кодогенерацией может ограничить возможности кастомизации, однако это, в свою очередь, заставляет команды работать эффективнее. В QIC мы постоянно улучшаем процессы и стремимся не создавать костылей. Если что-то идет не так, мы стараемся находить элегантные решения. А как вы используете кодген?

© Habrahabr.ru