Pull to refresh
0

Как мы обновляли мобильное приложение для официантов: выбор стека и тест трех версий. Кто победил?

Reading time7 min
Views5.5K

Привет! Меня зовут Сергей Арсёнов, я руковожу мобильной разработкой в компании r_keeper. Хочу рассказать, как мы обновляли мобильное b2b-приложение для официантов и почему выбрали для него не совсем классический стек — Kotlin Multiplatform Mobile + UI на Flutter. 

На чем писать?

Наше мобильное приложение используют официанты в ресторанах, где установлена система автоматизации r_keeper: оформляют через него заказы, добавляют и удаляют блюда, принимают оплату и т. д. Когда-то давно оно было написано на Java + Objective-C + WebView и заметно устарело: функциональность для iOS и Android полностью дублировалась, обе версии выглядели абсолютно одинаково, нативной верстки не было. Приложение с трудом поддавалось улучшениям, и в конце концов мы решили переписать его с нуля. Вопрос — на чем?

Мы точно понимали, что хотим, как минимум, уйти от WebView и неактуального стека. Плюс выбрать такой набор технологий, который позволит максимально быстро повторить текущий функционал приложения и существенно сократить Time2Market. Рассуждали так.

  1. Нужна технология, которая позволит не писать одну и ту же логику несколько раз под разные платформы (тем более для b2b-приложения, где UI и функционал для всех ОС идентичен). Получается, чистый натив не оптимален — нужна кроссплатформа.

  2. Это должна быть кроссплатформа, которая позволит сделать приложение для iOS, Android, а в перспективе и под web / desktop. К тому же нужно уметь поддерживать работу с платформой и железом, поскольку такие задачи могут возникнуть — PWA по этой причине выбыл. Выбыл и Xamarin — он не поддерживает web и вообще не очень быстр. Список сузился до нескольких вариантов: Flutter, React Native, KMM + UI на чем-нибудь (например, NativeUI) и Cordova.

  3. У кроссплатформы должен быть низкий порог входа для Android-разработчиков — это тот ресурс, которым мы располагали. Javascript они не знают — из-за этого выбыли React Native и Cordova. 

Какие плюсы и минусы?

В результате до финиша дошли варианты Flutter и KMM + NativeUI с какими-то комбинациями. Все — со своими достоинствами и недостатками.

  1. Во Flutter используется незнакомый язык Dart (правда, сильно похожий по синтаксису на известный каждому Android-разработчику Java) и декларативная верстка. Для Android-платформы декларативный Jetpack Compose еще не стал нормой, так что был риск, что этот вариант построения интерфейса снизит темпы разработки (как минимум на первоначальном этапе). Не было уверенности, что неопытный в этой технологии разработчик сможет качественно выстроить архитектуру и написать бизнес-логику. Мы также опасались, что кроссплатформа не позволит полностью реализовать все специфические особенности каждой платформы, вроде работы с push-уведомлениями или железом. Были сомнения и в достаточной производительности Flutter на слабых устройствах (такие нередко встречаются у корпоративных клиентов). Притом мы понимали, что у технологии есть свои преимущества: относительная зрелость Flutter SDK, единый язык и код для всех платформ и вариантов. К тому же поддерживал энтузиазм разработчиков, которым хотелось «пощупать» новую технологию.

  2. У варианта KMM + Native UI — много достоинств: это знакомый всем Kotlin, знакомый всем Android UI, плюс возможность вынести любой специфичный функционал в нативную часть. Минусом мы посчитали то, что UI пришлось бы писать на Swift и делать двойную работу по слою представления (а работать с Xcode не хотелось никому) — это грозило увеличением сроков разработки. Кроме того, технология KMM еще недостаточно зрелая — могли возникнуть проблемы.

  3. Можно было попробовать гибрид Flutter + KMM, но тут тоже были потенциальные «грабли». Мы не знали, как смогут общаться KMM и Flutter и насколько сложно будет собрать проект из таких непохожих частей. Правда, такая связка позволила бы не писать сложную логику на Dart и вообще делать UI только один раз, а все сложные вещи привычно написать на Kotlin.

Три версии на тест

Идеального варианта не было, поэтому мы решили пойти методом проб и ошибок. Сделали три версии тестового приложения с идентичным функционалом, чтобы оценить производительность каждого решения, сложность и время разработки, незамеченные пока ограничения технологий. Само приложение было несложным — оно грузило из небыстрой REST API список рецептов с описаниями и картинками и отображало его с разбивкой по категориям. Подборки были длинными (по несколько тысяч рецептов), а картинки — тяжелыми. 

Версию № 1 написали на чистом нативном Android (Clean Architecture, Android Jetpack, MVVM, Coroutines, Koin), № 2 — на KMM + Native UI, № 3 — на KMM + Flutter. Делать отдельное приложение на чистом Flutter не стали, потому что версия № 3 позволяла оценить и этот вариант.

Время и сложность создания нативного приложения (версия № 1) мы взяли за эталон для сравнения. Трудностей с его разработкой не возникло, создание с нуля заняло примерно одну неделю. Приложение, в соответствии с концепцией чистой архитектуры, было разбито на три слоя: представления, бизнес-логики и слоя данных. 

Для переписывания приложения на KMM (версия № 2) было необходимо следующее.

  1. Перенести слои Data и Domain в кроссплатформенную часть
    Для слоя Data пришлось переписать базу данных со стандартного Jetpack’овского Room на кроссплатформенный SQLDelight. Последний требует, чтобы таблицы и методы для общения с ними были описаны на варианте чистого SQL, путем создания .sq-файлов — при сборке именно по ним генерируются методы для обращения с базой данных. Готовых инструментов для миграции из Room в SQLDelight мы не нашли, так что пришлось вручную переносить сгенеренные таблицы из Room и писать SQL-запросы для методов — это заняло пару дней.

    Еще мы переписали сетевую часть с Retrofit на Ktor (сериализацию делали через kotlinx.serialization). Тут особых сложностей не возникло: для логики интерфейс общения с бэкендом остался прежним, была изменена лишь имплементация. Вообще для большей гибкости на Ktor можно использовать не только кроссплатформенные, но и нативные движки, например, OkHttp, но для наших целей хватило дефолтного.

    Слой Domain, естественно, перенесся в KMM без каких-либо изменений.

  2. Поменять способ общения между логикой и представлением на подходящий для кроссплатформы
    Для общения с логикой KMM-части был описан публичный интерфейс с методами, которые будут видны в слое представления, а сама KMM-часть реализована в виде некой библиотеки, которая имплементирует этот интерфейс. 

  3. Изменить слой представления для Android-версии, поменяв способ общения с логикой
    Потребовалось лишь переделать ViewModels для общения с логикой через изменившиеся интерфейсы

  4. Написать UI-часть под iOS
    KMM собирается как обычный Framework-пакет для iOS. Особых сложностей с этим не возникло — надо было лишь подключить эту библиотеку и написать стандартный UI на Swift, который вызывает из нее методы. 

В целом переделка проекта заняла около двух недель (не считая написания iOS UI), в основном оно ушло на знакомство с новыми библиотеками. Подводных камней на этом этапе мы не увидели.

Третьей и самой интересной стала версия № 3 на Flutter + KMM. Часть KMM с логикой бизнес-и дата-слоев без каких-либо изменений была перенесена из версии № 2 — нам потребовалось лишь написать UI на Flutter. 

Серьезные опасения насчет падения производительности при передаче больших массивов объектов между UI и KMM не оправдались — версия № 3 показала вполне приличную производительность и плавность даже на слабых устройствах с ARMv7 и старых iPhone. Никаких особых сложностей в освоении языка Dart у Android-разработчиков не возникло, а создать работоспособный UI для тестового приложения удалось за считанные дни. 

С другой стороны, стало понятно, что создание грамотной архитектуры для приложения на чистом Flutter может стать проблемой и затормозить разработку. 

Что мы поняли?

В итоге тестирования, освоения новых библиотек и работы с плагином KMM мы сделали вывод, что сложность версии № 2 примерно соответствует обычной классической платформенной (если не учитывать время, потраченное на освоение новых библиотек и написание UI-части для iOS). КММ помог четче разделить приложение по слоям и избежать их смешения. 

Мы пришли к интересной мысли, что можно даже сразу делать нативные Android-приложения на КММ, если планируется версия под iOS. Это практически не удлиняет разработку под Android, но зато экономит немало усилий при создании и тестировании iOS-версии — достаточно лишь написать UI-часть.

В целом тестирование убедило, что самым эффективным и быстрым в реализации решением для нас станет разработка приложения на связке KMM + Flutter. А если что-то пойдет не так, мы всегда сможем вернуться к резервному варианту Native UI + KMM — переделка из схемы Flutter + KMM затронет лишь слой представления. 

Пара слов о выбранных технологиях

Для тех, кто не знаком с выбранными нами технологиями, сделаю небольшое пояснение:

  • В качестве основы для слоя представления используется Flutter — SDK для создания приложений под iOS, Android и web (плюс desktop в планах). Это уже достаточно зрелая технология (релиз первой версии состоялся в 2015 году), с накопленной базой готовых решений и вопросами / ответами по ней. Впрочем, родная документация на сайте тоже очень подробная.

  • В качестве языка программирования для Flutter используется Dart. Он компилируется в бинарный код, что позволяет добиться высокой скорости выполнения операций, сравнимой с результатами Objective-C, Swift, Java или Kotlin. Dart не использует нативные компоненты платформ ни в каком виде: подобно игровым движкам он отрисовывает все графические элементы самостоятельно — с помощью собственного движка Google Skia. Все элементы этого интерфейса описываются декларативно, подобно новомодным Swift UI и Jetpack Compose. 

  • В качестве основы для бизнес-логики и, что немаловажно, дата-слоя используется Kotlin Muliplatform Mobile (KMM). Это SDK, созданный для упрощенной разработки кроссплатформенных приложений. Он построен на Kotlin Multiplatform (KM) — технологии, которая позволяет запускать на множестве платформ (Android, iOS, Linux, Windows, MacOS, web и т. д.) один и тот же код, написанный на Kotlin. При добавлении к KM интеграции с мобильными платформами получается KMM. Есть удобный репозиторий, где собраны основные библиотеки для KMM. Большинство компонентов KMM (за исключением интеграции с IDE и плагина под Android Studio) находятся в статусе stable или beta, так что риск использования данной технологии минимален, а ее API уже не будет меняться с новыми релизами. 

Если есть вопросы, буду рад ответить. А о реальном внедрении расскажу в следующей статье.

Tags:
Hubs:
Total votes 5: ↑5 and ↓0+5
Comments36

Articles

Information

Website
rkeeper.ru
Registered
Founded
Employees
201–500 employees
Location
Россия