Автоматичне генерування ресурсів із Figma для Compose
Вітаю! Мене звати Володимир, я Android-розробник. Нам часто потрібно автоматизовувати процеси, що можуть бути рутиною та займати багато часу, якщо все робити вручну. На попередньому проєкті ми мали White-Label-рішення, що брало за основу одну кодову базу та містило різні UI-кастомізації для клієнтів.
В цьому пості ми розглянемо різні підходи та інструменти, які допомогли нам відразу перейти до написання коду, не витрачаючи час на пошук та правку змін від дизайнерів.
FIGMA WHATEVER DSM
Почнемо з простого. Дизайнери, як і програмісти, також намагаються в DRY та придумують для цього чудові інструменти, як Design System Manager. Спочатку визначаються компоненти, що можуть повторюватись в проєкті, і розробляється структурований набір кольорів та стилів. Тому якщо будь-який колір чи стиль зміниться в одному місці, він автоматично змінюється по всьому проєкту. Зручно, чи не так? У Figma чи будь-якому іншому сервісі є можливість експортувати такі конфігурації в спеціальний файл, який ми потім будемо аналізувати та генерувати на його основі код.
Якщо ви працюєте з Jetpack Compose, то можете пригадати, що є такі класи як ColorScheme та TextStyle. З філософією Material у ColorScheme ми описуємо кольори, а TextStyle описує вигляд тексту, що використовуються компонентами для рендеру.
Кодогенерація
Якщо ви використовували такі бібліотеки як Room, Glide або будь-які інші, що потребували кодогенерації, ця схема вже трішки для вас знайома.
В Java можна зустріти назву anation processing, в Kotlin — kapt чи ksp. Загалом, це — функція компілятора, яка допомагає нам проаналізувати код та згенерувати додаткові файли. Але оскільки ми не можемо на json-файли повісити анотації, і це не є kotlin-файл, тут потрібне інше рішення. Тож ми з вами навчимося розробляти плагін, що приймає на вхід дані, шукає потрібний файл та генерує необхідні класи.
Gradle Plugin
Існує багато варіантів того, як зробити gradle plugin, починаючи від написання задач в головних gradle-файлах до розміщення в buildSrc
, тому скористаємось рекомендаціями, а саме — Composite Builds.
Суть проста — плагіни лежать окремо, код застосунку — окремо, потім це все залишається поєднати. Тут можна переглянути дуже багато прикладів того, як AGP використовує цей механізм. Та і взагалі рекомендую ознайомитися, можливо знайдете відповідь на питання, яке вас давно хвилювало.
Ось так буде виглядати структура модуля, в якому буде розроблятися плагін:
Нічого екстраординарного тут нема, різниця тільки в тому як він підключається на найвищому рівні — завдяки інструкції includeBuild()
:
В build.gradle.kts
ми маємо описати залежності, які потрібні для роботи плагіну, та деякі базові налаштування:
Gradle Plugin Implementation
Отож, що ми маємо зробити?
- Проаналізувати структуру DSM-токенів та підготувати для цього відповідні моделі.
- Знайти всі можливі флейвори, в яких лежать токени, що описують дизайн кожного клієнта в проєкті.
- Якимось чином проаналізувати ці дані та навчитися генерувати потрібні файли.
- Зробити зручно і це обернути в gradle-таски?
- ...
- Писати код!
Як ми бачимо, у файлі є опис кольорів і вони каскадно можуть складатися з кількох логічних рівнів, де останній містить інформацію, що нас цікавить. Зазвичай ресурси мають подібну структуру, де вказується їх тип та значення.
{
"color": {
"m3": {
"white": {
"description": "",
"type": "color",
"value": "#ffffffff",
"blendMode": "normal"
},
"black": {...},
"sys": {
"light": {...},
"dark": {...}
},
"ref": {...},
"key-colors": {...},
"source": {...},
"surfaces": {...},
"state-layers": {...}
}
},
"font": {...},
"typography": {...}
}
Стосовно кольору нас цікавить значення, тому відповідний клас буде містити його hex-представлення.
Gradle tasks
Тут трішки варто розказати про їх створення. Якщо вам потрібна гнучкість у розробці, зазвичай ви будете програмно описувати задачі за схожою структурою.
При реєстрації ми маємо заповнити всі поля, що позначені анотаціями, а саме — шлях до json-файлу, шлях до теки нашого флейвору та фінальний package згенерованого файлу.
Для полегшення собі життя ми деякі речі захардкодимо, але якщо потрібна більша гнучкість — варто глянути в сторону gradle extensions.
Оскільки ця задача є абстрактною, деякі конфігурації ми залишаємо за реалізацією, тому вигляд для задачі, що генеруватиме файл з кольорами, буде таким:
Щоб наші задачі можна було запустити, їх варто зареєструвати в проєкті.
Для цього нам спочатку потрібно за допомогою плагіну визначити, які є build variants
у проєкті, та зареєструвати задачу кожного типу.
А тепер перейдімо до найцікавішого — як же відбувається генерування файлів?
Для цього нам потрібно використати такий інструмент, як KotlinPoet. Він дозволить доволі просто створювати файли, що нам потрібні. Весь механізм складається із різного роду білдерів, якими ми додаємо типи файлів, їхні властивості, значення тощо.
Вся суть нашої задачі полягає в тому, щоб розпарсити json, отримати потрібні дані та записати у відповідні файли.
Оскільки ми знайомі зі структурою json-файлу, то можемо написати функцію, яка буде ходити по ньому та збирати дані. В кінцевому результаті це буде мапа з ключами-назвами полів та значеннями-модельками.
Коли ж ми отримали список всіх полів, використовуючи KotlinPoet описуємо, що бажаємо створити файл, який міститиме object
з полями, що було зчитано. В кінцевому результаті в нас мають вийти файли такого вигляду:
Використовуючи згенеровані файли ми можемо описати нашу тему.
За потреби ми можемо використовувати стилі та кольори напряму.
От і все, наше початкове завдання виконано. Тепер ми маємо змогу завантажувати файл до проєкту, запускати задачу й отримувати файли, що вже можна використати у розробці.
Те, що раніше робилося вручну і могло містити помилку через людський фактор, наразі працює як годинник та не потребує додаткових залежностей (поточне iOS та попереднє Android-рішення вимагало встановлення тонни Node.JS-модулів, щоб запустити скрипт, який до кінця не виконував свої задачі та все одно потребував правок розробника).
Неочевидні проблеми
Розробити плагін — це пів біди. У нас завжди буде щось, що піде не так.
- Через складність проєкту
мидизайнери не використовували Material правильно і розробили десятки, якщо не сотні власноіменованих кольорів та стилів. Через це використання звичайного MaterialTheme не було можливим, а наш плагін генерував кастомні ColorScheme та Typography-класи. - Відносно багато часу пішло на початкову розробку та міграцію, проте, якщо у вас чистий Material і не треба з дизайнерами сидіти годинами для розв’язання питань іменування токенів, це не має бути проблемою.
- Всі
@Preview
треба було огортати в нашу тему, оскільки вона ламалась під час побудови. - Проблемою можуть бути власне дизайни — один з клієнтів мав кнопку з градієнтом, хоча конкретно для цього токену мав бути колір. Все вирішилося елегантним
костилемрішенням, але ми дуже попросили дизайнерів уникати таких конфліктів в майбутньому. - Складність конфігурації — сам процес хоча і straight-forward, такі косяки, як попередній, може потребувати солідної зміни логіки роботи плагіну чи структури моделей.
А на цьому все. Напишіть в коментарях, які ви будували подібні рішення або ж якщо знаєте, як не писати власну тему кожного разу для перегляду Preview
. Якщо комусь потрібен код, то весь проєкт лежить тут.
12 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів