Всем привет, меня зовут Ибрагим, я iOS разработчик одной из команд мобильного банкинга и это моя первая статья для Хабра, поэтому прошу строго не судить.
Сегодня хочу рассказать Вам, как мы пилили дробили монолит на SPM пакеты и создавали дизайн систему.

Как все начиналось
Все началось в одень "прекрасный" день, когда нас снова удалили с AppStore и перед нами стояла цель научиться быстро видоизменять наше приложение, как функционально, так и визуально, чтобы снова опубликоваться в “яблочном” магазине.
Чтобы провернуть такой план, мы решили, раздробить монолит на SPM пакеты. Для себя мы выделили три первых модуля, с которых все началось:
“Ядро”
“UI Компоненты”
“Компоненты приложения”
О “Ядре” и “Компонентах приложения” мы поговорим в следующих статьях, если эта статья Вам понравится и будет интересно узнать о нашем опыте. Скажу, что в “Ядро” мы вынесли всю работу сетевого слоя и работу с координаторами (изолировали отдельным таргетом внутри пакета). В “Компоненты приложения” мы поместили универсальные экраны которые используются по всему приложению, например экран ввода СМС-кода, экран отображения успешной операции и т. п.
Хранение цветов и шрифтов в приложении
Прежде чем приступить к разработке модуля “UI Компонентов”, мы в первую очередь пошли к команде дизайнеров согласовывать нашу будущую дизайн систему, потому что без тесной совместной работы с обеих сторон, создать дизайн систему невозможно. В результате многочасовых споров совещаний, мы выработали ряд правил и договоренностей, следовать которым должна была вся команда, например, одни из которых:
все новые экраны в дизайне будут строиться только на компонентах;
количество компонентов будет ограничено;
новые компоненты будут согласовываться с разработчиками;
не будут создаваться универсальные компонентом для всех нужд;
компоненты будут стилизоваться только при помощи стилей, например цвет элемента будет задаваться не напрямую через hex, а через стиль Text.main, который в свою очередь равен определенному цвету, аналогичная история и со шрифтами.
На выходе, в Figma мы получили два файла, где лежат все цвета приложения на которые ссылаются компоненты при создании дизайна и точно также со шрифтами.
В коде основного таргета приложения, мы в точности повторили две данные структуры UIColors.Branding (отвечает за цветовую палитру) и UIFonts.Brandings (отвечает за шрифты).


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

Вы спросите, почему бы не поместить цвета и шрифты внутрь модуля “UI Компонентов”? Иногда на проекте случается редизайн, а когда в проекте не одна сотня экранов (в том числе и легаси), редизайнить все приложение становится очень трудозатратно, поэтому такие процессы происходят постепенно, в несколько релизов. Поэтому для этого создается новая структура для цветов и шрифтов и некоторое количество релизов у вас живет несколько схем. Но Вы скажите, не убедил, все равно можно хранить данные структуры внутри модуля. Рассмотрим другой случай, у нас несколько приложений и все они используют модуль “UI Компонентов”, но каждое приложение имеет свои брендовые цвета и шрифты, в данном случае идеальным вариантом является хранить цвета и шрифты внутри самого приложения.
Настройка внешнего вида ui-элементов
Прежде чем идти и верстать наши компоненты нам пришлось научиться применять стили к нашим компонентам. Можно было взять подготовленные структуры цветов и шрифтов и напрямую использовать в компонентах. Данный подход чреват тем, что компоненты не выйдет видоизменять. Также можно было передавать все необходимые цвета, шрифты как параметры в компонент. Но что делать когда нужно ограничить UILabel до трех строк или сделать закругление у UIView, передавать снова параметрами? Эта были бы мега конструкторы, которые не удобно использовать в работе и тем более их поддерживать.
Выпив пару чашек кофе, решили, что необходимо создать публичную структуру Appearance для каждого базового ui-элемента (UILabel, UIButton, UITextField и т.д.) внутри модуля “UI Компонентов”. Данная структура содержит все возможные свойства внешнего вида определенного элемента, которые могут быть изменены разработчиком.

Для удобства использования также была создана публичная функция apply, которая в качестве параметров принимает Appearance и применяет переданные свойства к ui-элементу.

Создание компонентов дизайн системы
После предыдущих подготовок, мы приступили к разработке компонентов дизайн системы. Каждый компонент у нас имеет три составляющих:
Публичную структуру Appearance - содержит все Appearance для используемых ui-элементов
Публичную структура Layout - содержит все отступы и размеры необходимые для верстки ui-элементов
Публичную структура Content - содержит данные для отображения и также передает callback действий если необходимо.



Для Layout мы подготовили структуру Spacings, куда вынесли допустимые размеры верстки (кратные двум): XXS - 2pt, XS - 4pt и т. д. Но не согласовали этот момент с командой дизайна и в продакшн это не попало, поэтому это наш небольшой технический долг, который остался после разработки дизайн системы и за который мы скоро возьмемся (наверное, но это не точно).
Соблюдая данный принцип мы наверстали для себя более пятидесяти ui-компонентов, начиная от компонентов для отображения суммы, чек боксов, заканчивая компонентом диалога.
Теперь при разработке нового экрана, в процентах восьмидесяти нам не требуется занимается версткой компонентов, а необходимо лишь выделить компонент Figma, узнать его название, найти соответствующий компонент в нашем модуле, вызвать его передав необходимый Appearance и Layout. Готово! Согласитесь, это лучше чем верстать каждый раз, когда нужно создать новый экран. Остальные двадцать процентов - это компоненты, которые относятся к определенному функционалу и не выносятся в общую дизайн систему.
Хранение стилей в приложении
Для стилей кнопок мы сразу создали два стиля primary и secondary, в каждый из стилей описали состояния внешнего вида: normal, selected, highlighted, disabled, focused.
По началу все другие Appearance компонентов у нас хранились прямо в фабрике стилей к каждому определенному сценарию. Но после нескольких релизов, заметили что стало очень много дублирующих стилей, которые переносились из функционала в функционал.
После чего, решили создать в приложении extension для используемых Appearance компонентов. Теперь нам не нужно писать инициализаторы стилей каждый раз, а необходимо всего лишь вызвать необходимый стиль - UILabel.Appearance.default. После данных манипуляций, на выходе получили папку с расширениями Appearance компонентов, в которой содержатся все стили нашего приложения.
На этом статья подходит к концу, спасибо, что уделили время, надеюсь, что статья понравилась и наш опыт будет Вам полезен!
Bardakan
Допустим, вам нужно создать новый экран в красных тонах. У вас в assets уже штук 10 оттенков красного. Вы заставляете дизайнеров использовать строго одни и те же названия цветов? Каждый раз ищете по ассетам, добавлен ли там уже цвет с нужны rgb?
По этим причинам предпочитаю хранить цвета в коде, но может есть вариант обойти это
ib-khasanov Автор
Спасибо Вам за вопрос!
В нашем случае, если новые красные тона - это стиль одного экрана или компонента, то будет расширена цветовая палитра дизайнерами, а потом и нами в коде (за уникальностью добавляемого цвета, следят дизайнеры).
Если предполагаем, что красные тона - это будущий стиль всего приложения, то будет создана новая цветовая палитра (вначале дизайнерами, потом и нами в коде).