Привет, меня зовут Елена Яновская, 5 лет в iOS-разработке, TechLead iOS в Альфа-Банке. Участвую в разработке главного экрана и активно продвигаю использование SDUI в фичах. Контекст: для устранения проблем с виджетами, о которых Сергей рассказал в прошлой статье Как катить фичи без релизов. Часть 1: про виджеты, было решено разработать более низкоуровневый и гибкий подход для динамической отрисовки UI мобильного приложения с сервера.  Низкоуровневый Server Driven UI мы решили сделать максимально приближенным к дизайн-системе Альфа Мобайла, чтобы с его помощью отрисовать любой UI, который нам позволяет сделать дизайн-система. Например, если дизайнер указал, что цвет фона в данном компоненте кастомизируется, то SDUI модель для этого компонента должна, по умолчанию, уметь настраивать цвет с бэкэнда. Аналогично со всеми другими свойствами компонентов, например, со шрифтом или его размером. Дизайн-система Альфа Мобайла имеет довольно обширную библиотеку UI-компонентов, она состоит из различных вью и врапперов для вью. При этом у каждого из компонентов могут быть свои различные визуальные вариации, например, у кнопки ниже может быть разный цвет фона, наличие иконки и размер кнопки. Все эти компоненты и их вариации на iOS и на Android существуют уже давно. Чтобы реализовать ServerDrivenUI, нам оставалось научиться их динамично и гибко конфигурировать с бэкенда. Базовые енамы. В основе нашей дизайн-системы лежат такие минорные единицы, как цвет, типографика и спейсинги. Значения этих единиц строго зафиксированы и имеют свой нейминг. Эти значения мы в первую очередь поддержали в нашем новом SDUI. Цвета, типографику и спейсинги мы представили в виде енамов, значения которых совпадают со строками, которые нам может прислать сервер. Атомы. Также у нас есть атомы — это базовые UI-элементы, из них построить интерфейс нельзя, но они входят в состав UI-компонентов. Например, это атомы Text и Icon.  У каждого атома есть свои свойства. Например, для атома Text мы можем задать типографику, цвет, которые у нас представлены в виде енамов, и, непосредственно, value — значение самого текста. У Icon тоже есть свойство цвета, а также url, либо name картинки, в зависимости от того, откуда мы хотим её загрузить. Проще говоря, чтобы не дублировать свойства, мы их инкапуслировали в модель атома. Компоненты. И, наконец, в SDUI есть модели для самих компонентов, которые могут состоять из других компонентов. Например, для описания DataView (ниже), нам нужно описать два её вложенных компонента DataContent и IconView.  DataContent состоит из трёх атомов текста. IconView имеет атом Icon, а также какие-то дополнительные свойства, как Size и Shape. Таким образом, здесь мы можем увидеть конечную иерархию SDUI модели для DataView. Она имеет много вложенностей: сама DataView состоит из двух компонентов; каждый из компонентов уже может состоять из атомов; а атомы могут иметь значения из енамов. Такая иерархичность SDUI-модели каждого компонента дизайн-системы позволяет нам с самого начала заложить максимальную гибкость для UI. Ответы с сервера в мобильном приложении Альфа-Банка приходят в виде JSON. В нём же теперь может прийти конфигурация для нашего UI, например, для DataView. Для упрощения понимания этой системы среди разработчиков и аналитиков Альфа Мобайла, мы добавили previewer в отдельное iOS-приложение. Previewer — это механизм, чтобы прямо в рантайме подогнать дефолтный JSON под нужную нам view, меняя JSON и проверяя изменившееся отображение view в соседней вкладке.  Промежуточные итоги. Изначально у нас был фреймворк для UI-компонентов. В нём лежали сверстанные view и вью-модели для них, с помощью которых мы могли сконфигурировать эти view. Этот фреймворк остался неизменным.  Но дополнительно у нас появился фреймворк SDUI, в котором лежали енамы, атомы и DTO-модели для наших UI-компонентов. Там же появились мапперы: они брали на вход DTO-модель и возвращали сконфигурированную вью-модель. Теперь возникает вопрос — как нам расположить полученную view относительно других элементов на экране? Умеет ли наш фреймворк в Layout? У нас есть 2 инструмента: StackView и ConstraintWrapper. StackView соответствует нативным UIStackView на iOS и LinearLayout на Android. Он позволяет нам построить элементы в линейном лэйатуе. На картинке показано, как в теории мы могли бы присылать с бэкенда view для обмена валют, представив её в виде стэка из вложенных вертикальных стэков, в каждом из которых по три текста. Как и в нативном UIStackView, в нем есть поле alignment (выравнивание элементов). На примере ниже оно установлено как fill — заполняет все свободное пространство.  Если заменить значение на end, то элементы сдвинутся к правому краю. ConstraintWrapper — это инструмент для построения лэйаутов на ограничениях интерфейса (констрейнтах), он соответствует AutoLayout на iOS и ConstraintLayout в Android. В нём есть модель с атрибутами ограничений, которые можно поделить на 2 типа: относительные и фиксированные. Относительное  ограничение — ограничение относительно какого-то элемента. Чтобы задать элемент, относительно которого нам нужен констрейнт, в поле reference мы ставим тег этого элемента. В поле constant мы указываем значение смещения по горизонтальной или вертикальной оси. Фиксированные ограничения нужны для ширины и высоты. Для них указывается лишь constant, то есть само значение ширины или высоты элемента. Как же этот ConstraintWrapper описать JSON-ом? Представим, что нам надо расположить 8 элементов в виде ромбика. У каждого элемента есть свой набор констрейнтов. Рассмотрим на примере A и F, как это происходит в ConstraintWrapper.  Для А нам нужно, чтобы: его верхний край соответствовал верхнему краю его superview со смещением 5; он располагался по оси Х относительно родителя; и его ширина была равна 50.  В массиве для ограничений этого элемента мы указываем в type тип ограничения top с константой 5, centerX с нулевой константой, а также width c константой 50. Для относительных констрейнтов в reference указываем superview — относительно родителя. Для F нам нужно построить констрейнты относительно элемента А: верхний край должен соответствовать нижнему краю первого элемента со смещением 5; левый край должен соответствовать правому краю первого элемента со смещением 5. У первого элемента «А» тэг был ButtonView1, соответственно, в JSON-е у поля reference будет значение ButtonView1. Аналогично строим массив констрейнтов для остальных элементов. Отдельное внимание хочется уделить базовому элементу Server-Driven UI — LayoutElement. Это общая сущность, объединяющая все поддерживаемые в SDUI UI-компоненты. Она нужна для разных целей, где нельзя заранее определить тип присылаемого компонента, например, для StackView. Бонусом этот LayoutElement позволяет нам с сервера прислать экшен, который обработается, как только пользователь кликнет по элементу. Например, мы можем указать диплинк или разметку аналитики, которая будет отправлена после клика.  У нас появилось много сущностей, в которых легко запутаться — нужна документация. В качестве аннотации для всех SDUI моделей была выбрана JSON-схема. Это не полноценный JSON response, а скорее способ его описания.  JSON-схема состоит из базового описания сущности: name для названия объекта, type - это либо object, либо enum,  и description для описания сущности на русском языке.  Также в него входят характеристики каждого поля, например, указание обязательности поля и снова description. Эти JSON схемы также позволили нам добавить на фронт и бэкенд кодогенерацию моделей. JSON схемы лежат в отдельном репозитории. Сам процесс добавления нового компонента в SDUI состоит из трёх шагов: Сначала разработчик составляет JSON-схему для компонента, согласует её с дизайнером. Затем он проходит код ревью своей схемы, в ходе которого ему нужно получить по 1 аппруву от iOS, Android и Backend-разработчика.  И уже после этого он пишет реализацию для своей платформы. Такой процесс позволяет нам всегда сохранять документацию в виде JSON-схем актуальной, и использовать этот репозиторий как единую точку правды. Виджеты и SDUI — это две разных системы и два разных фреймворка.  Виджеты могут отвечать и за логику, и за UI, а SDUI только за UI. Как же нам сделать виджет с SDUI? Для этого у нас разработаны виджеты SDUI Widget, VerticalListWidget для вертикального списка элементов и HorizontalListWidget для горизонтального списка элементов. Связка с SDUI происходит за счёт того, что его поле content маппится к нашему универсальному LayoutElement (о нём я рассказывала выше), который может быть как единичным элементом, так и стеком из других элементов. Благодаря этому, мы можем прислать абсолютно любой UI в наш виджет.  На картинке выше представлен пример для JSON response такого SDUI виджета, поля из его верхней части обрабатываются самим виджетов, а содержимое в поле content обрабатывает SDUI фреймоворк. Он принимает на вход эту структуру и возвращает уже полностью сконфигурированную view, готовую для её отображения в виджете. Таким образом, SDUI Widget позволяет изначально предусмотреть все возможные конфигурации его вложенных view, тем самым сокращая количество копипасты и доработок на фронте, решая большую часть проблем виджетов. Виджеты — это фреймворк для динамического управления отображением фичей на экране-хабе. Server Driven UI — это язык на базе JSON, позволяющий сконфигурировать и отрисовать View. В зависимости от фичи, мы можем использовать разный уровень абстракции. Меньше копипасты в реализации UI виджета. Можно сделать разную верстку при Zero coding. К сожалению, SDUIWidget не везде применим. Для фичей с нестандартным UI, скорее всего, придется создавать новый виджет. Если же нам нужна простая верстка из набора UI-компонентов нашей дизайн-системы, то тут идеально подойдет SDUIWidget. И, что важно, так как ServerDrivenUI - это независимый фреймворк, его можно использовать и на обычных фичевых экранах. Используете ли вы подобные виджеты у себя? А может у вас есть, что добавить по теме? Напишите в комментариях, будет интересно.  Рекомендованные статьи: Как катить фичи без релизов. Часть 1: про виджеты Как настраивать аналитику, если у вас Backend Driven UI Эволюция Server-Driven UI: динамические поля, хэндлеры и многошаг Хочу в iOS-разработку: к чему готовиться на собеседовании в продуктовую команду Багатон как в первый раз Как мы ведём требования к ПО: формализация Почему мы ошибаемся при первоначальной оценке фич? Как перестать волноваться и полюбить хакатоны Также подписывайтесь на Телеграм-канал Alfa Digital — там мы постим новости, опросы, видео с митапов, краткие выжимки из статей, иногда шутим.  iOS Tech Lead