Привет, меня зовут Елена Яновская, 5 лет в iOS-разработке, TechLead iOS в Альфа-Банке. Участвую в разработке главного экрана и активно продвигаю использование SDUI в фичах.

Контекст: для устранения проблем с виджетами, о которых Сергей рассказал в прошлой статье Как катить фичи без релизов. Часть 1: про виджеты, было решено разработать более низкоуровневый и гибкий подход для динамической отрисовки UI мобильного приложения с сервера. 

Низкоуровневый Server Driven UI мы решили сделать максимально приближенным к дизайн-системе Альфа Мобайла, чтобы с его помощью отрисовать любой UI, который нам позволяет сделать дизайн-система. Например, если дизайнер указал, что цвет фона в данном компоненте кастомизируется, то SDUI модель для этого компонента должна, по умолчанию, уметь настраивать цвет с бэкэнда. Аналогично со всеми другими свойствами компонентов, например, со шрифтом или его размером.

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

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

Визуальные состояния на примере ButtonView.

Все эти компоненты и их вариации на iOS и на Android существуют уже давно. Чтобы реализовать ServerDrivenUI, нам оставалось научиться их динамично и гибко конфигурировать с бэкенда.

Сущности SDUI

Базовые енамы. В основе нашей дизайн-системы лежат такие минорные единицы, как цвет, типографика и спейсинги. Значения этих единиц строго зафиксированы и имеют свой нейминг. Эти значения мы в первую очередь поддержали в нашем новом 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?

Layout SDUI элементов

У нас есть 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 - это независимый фреймворк, его можно использовать и на обычных фичевых экранах.

Используете ли вы подобные виджеты у себя? А может у вас есть, что добавить по теме? Напишите в комментариях, будет интересно.


Рекомендованные статьи:

Также подписывайтесь на Телеграм-канал Alfa Digital — там мы постим новости, опросы, видео с митапов, краткие выжимки из статей, иногда шутим.