Как стать автором
Обновить

Server-driven UI, «Триплекс» и JSON: как Сбер сервисы в мобильные приложения выводит

Время на прочтение6 мин
Количество просмотров4.1K
Всего голосов 12: ↑11 и ↓1+10
Комментарии9

Комментарии 9

Скажите пожалуйста, у Вас до сих пор свайп вниз не сам получает баланс, а запрашивает отправку пуша с балансом?)

Вы со сболом не путаете?

Да, сбер-бизнес не использовал, если тут не так, то может быть расскажут команде СБОЛ, как делать правильно)

Спасибо за статью. Расскажите, пожалуйста, как вы работаете с формами ввода.

  1. Как и откуда тянутся данные для условного dropdown?

  2. Можете привести пример, как сейчас выглядит блок events для связанных полей? Если выбор в одном dropdown должен повлиять на данные в другом dropdown. Например, выбор города в одном dropdown, а другой dropdown должен отображать список улиц (в идеале подгружать с сервера справочник улиц для выбранного города).

  3. Как работает хендлер Submit? Есть ли какой-то контракт, описывающий какой объект (JSON) должен отправляться из формы на сервер и как форма должна реагировать на условный 400 Bad Request?

Отвечу по пунктам.

1. Модель ответа сервера сразу включает в себя описание экрана с бизнес-данными. Обновление данных на форме происходит путём обновления экранной формы или компонента с помощью ответа от сервера.

2. Расскажу на примере двух текстовых полей и пуллеров. Снизу JSON, который демонстрирует похожую реализацию.

{
    "screenId": "Settings",
    "navigation": {
        "title": "Адрес клиента"
    },
    "fieldSet": [
        {
            "controlId": "textFieldCity",
            "component": "TEXT_FIELD",
            "text": "Москва",
            "events": [
                {
                    "event": "TAP",
                    "action": {
                        "type": "SHOW",
                        "receiver": "pullerChooseCity"
                    }
                },
                {
                    "event": "CHANGE",
                    "action": {
                        "type": "SCREEN",
                        "subtype": "REPLACE",
                        "url": "rest\/v3\/screen\/ChosenCity",
                        "body": {
                            "city": "pullerChooseCity"
                        }
                    }
                }
            ]
        },
        {
            "controlId": "pullerChooseCity",
            "component": "PULLER",
            "data": [
                {
                    "title": "Москва",
                    "pullerId": "50102"
                },
                {
                    "title": "Санкт-Петербург",
                    "pullerId": "12300"
                },
                {
                    "title": "Самара",
                    "pullerId": "00000"
                }
            ],
            "events": [
                {
                    "event": "TAP",
                    "action": {
                        "type": "CHANGE",
                        "receiver": "textFieldCity"
                    }
                }
            ]
        },
        {
            "controlId": "textFieldStreet",
            "component": "TEXT_FIELD",
            "events": [
                {
                    "event": "TAP",
                    "action": {
                        "type": "SHOW",
                        "receiver": "pullerChooseStreet"
                    }
                }
            ]
        },
        {
            "controlId": "pullerChooseStreet",
            "component": "PULLER",
            "data": [
                {
                    "description": "Щусева улица",
                    "pullerId": "2"
                },
                {
                    "description": "Малая бронная улица",
                    "pullerId": "1"
                },
                {
                    "pullerId": "5",
                    "description": "Арбат улица"
                },
                {
                    "description": "Кутузовский проспект",
                    "pullerId": "6"
                },
                {
                    "description": "Люблинская улица",
                    "pullerId": "999"
                }
            ],
            "events": [
                {
                    "event": "TAP",
                    "action": {
                        "type": "CHANGE",
                        "receiver": "textFieldStreet"
                    }
                }
            ]
        }
    ]
}

Этот JSON и есть ответ сервера при открытии экранной формы. Что он содержит?

В текстовом поле textFieldCity прописывается событийная обработка: при тапе на него нужно показать пуллер выбора города – pullerChooseCity. В «событийке» пуллера прописано, что нужно изменить текстовое поле textFieldCity значением, на которое тапнул пользователь в пуллере. В этом случае у нас произойдёт простое заполнение текстового поля выбранным значением. Однако для того, чтобы подгрузить улицы для выбранного города, необходимо провести сетевой запрос, который обновит экранную форму. Для этого у текстового поля textFieldCity в «событийке» есть событие CHANGE, которое срабатывает при изменении текстового поля. В этом случае при изменении нужно вызвать запрос POST rest\/v3\/screen\/ChosenCity и ответом от него произвести операцию над экраном SCREEN – REPLACE. Соответственно, с точки зрения разработки мы ожидаем, что с бэка придёт экранная форма с текстовым полем textFieldStreet и пуллером pullerChooseStreet, актуальными для выбранного города в текстовом поле textFieldCity.

3. Что касается передачи данных с формы мобильного приложения на бэк – как такового жёсткого контракта нет. В любом действии, которое подразумевает сетевой запрос, есть объект "body", который представляет из себя формат type obj = {[keys: string]: any};. При этом данные из конкретного компонента будут подставляться по его идентификатору. На примере кейса выше:

{
                    "event": "CHANGE",
                    "action": {
                        "type": "SCREEN",
                        "subtype": "REPLACE",
                        "url": "rest\/v3\/screen\/ChosenCity",
                        "body": {
                            "city": "pullerChooseCity"
                        }
                    }
}

В этом случае выполняется вот такой запрос на бэк (лог из мобильного приложения):

https://{some-host.com}/rest/v3/screen/ChosenCity 
request.headers = {
Accept-Language = ru
Content-Type = application/json
Content-Length = 16
x-sber-date = 2023-01-24T13:57:46+03:00
Accept-Encoding = gzip
}
request.httpMethod = POST
request.body = {
"city" : "50102"
}

В случае возникновения транспортной ошибки (недоступность бэка) на мобильном приложении будет показана стандартная ошибка с шаблонным текстом. Все бизнес-ошибки мы рекомендуем командам обрабатывать двумя способами:

1. Отрисовывать новый экран, где начнётся альтернативный сценарий или подсветятся проблемные зоны.

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

{
   "code": 1001,
   "message": "Произошла ошибка."
}

Что вы имеете в виду, говоря о хендлере SUBMIT?

Судя по описанию, на базе текущих компонентов можно сделать что-то более-менее простое.

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

Отсюда вопросы :)

Что за продукты вы уже вывели и какие планируете выводить? Можно посмотреть на эти экраны - насколько они зрелые и интересные по дизайну?

И кто, как не нативщики, делают новые компоненты для платформы? (точно ли тут есть экономия?)

Какое сейчас у вас соотношение тех, кто делает натив, и кто разрабатывает продукты на SDUI?

Что будете дальше делать со сложными интерфейсами, которые сейчас не можете реализовать с помощью SDUI, а конкуренты за счет натива могут?

Сейчас в части продуктов на этом решении у нас выведены «Подбор кредита», «Подключение АУСН», раздел «Налоги и взносы» в виджете «Мои дела» и список заказов для отраслевого решения «Транспорт».

Интерес к решению внутри банка достаточно высок у команд, которые испытывают трудности в найме мобильных разработчиков, поэтому новые продукты на SDUI появляются каждый квартал. Всё правильно, UI и UX в случае применения SDUI достаточно ограничен, не всё можно реализовать с таким подходом. Уже реализованные экраны как раз представлены в статье в разделе «Результаты». Конечно, это не всё, но принципиально именно эти экраны мы сейчас умеем делать.

Безусловно, именно мобильные разработчики создают фреймворк в СберБизнес. Сейчас в команде, которая делает решение, по два человека разработки с iOS и Android соответственно. Нативные формы у нас пилят несколько десятков команд, в каждой примерно по одному разработчику iOS и Android. Вопрос экономии очень относителен. Те, кто уже вывел формы на нативе, не особенно хотят переезжать на SDUI, но это и не требуется. Однако, как упомянуто в статье, примерно 80 % существующих экранов можно сделать на нашем фреймворке. То есть у восьми из десяти новых команд, которые хотят появиться в СберБизнес со своим продуктом, нет необходимости в найме мобильных разработчиков для старта MVP и проверки ценности функциональности в мобильном формфакторе.

Мы активно развиваемся и наполняем фреймворк новыми фичами на основе CR от команд и развития дизайн-системы. Какие-то сценарии реализовать на SDUI не получится, и нужно прибегать к нативным разработчикам. Это решение мы оставляем на откуп продуктовым командам. По опыту, команды руководствуются P&L продукта в мобильном приложении. Если рентабельность продукта или её перспективы позволяет нанять разработчиков, то нативу быть!

Возможно, это опечатка: «При этом у кнопок также есть состояния «кликабельна» или «некликабельна», то есть основная или альтернативная».

Обычно в дизайн-системах кнопки могут быть основными и альтернативными (второстепенными, третьестепенными), но при этом каждая из них может быть кликабельной и некликабельной (disabled). А в этом предложении как будто приравнены кликабельные кнопки к основным, а некликабельные к альтернативным.

Да, конечно. У нас действительно есть основные и альетрнативные кнопки. И тот и другой тип могут быть как кликабельными так и некликабельными)

Спасибо, что заметили!

Зарегистрируйтесь на Хабре, чтобы оставить комментарий