Допустим, перед вашей командой стоит задача разработать мобильное приложение для крупного eCommerce проекта. Современные технологии развиваются столь стремительно, что порой непросто отдать предпочтение той или иной технологии. Какие-то из них набирают обороты, некоторые уже находятся на пике своего развития, а есть и те, которые постепенно угасают, уступая место более продвинутым решениям.
Наша команда должна была не просто создать проект мобильного приложения, но и сформировать задел на будущее, чтобы нам не пришлось удерживать устаревший проект на плаву, а можно было бы легко и просто интегрировать передовые технологии в области разработки мобильных приложений в наше готовое решение.
В этой статье мы разберем основные шаги, которые вам предстоит пройти, создавая свое мобильное приложение и поделимся накопленным опытом кроссплатформенной разработки.
Выбор инструментов разработки (Flutter)
Сперва необходимо определиться с выбором платформы/фреймворка, на котором предстоит разрабатывать проект.
Мы анализировали варианты в том числе и с точки зрения бизнеса. На самом деле выбор стоял между двумя направлениями:
Создание нативного приложения, а по факту это должно быть два приложения: одно для Android и второе для iOS. По сути, это вариант “Качественно и Дорого”, поскольку скорость выведения на рынок готового решения определенно больше, чем у конкурирующего подхода (то же самое касается и скорости выхода обновлений). Это направление больше подходит для очень крупных компаний, которые могут себе позволить содержать две команды разработчиков (Swift и Kotlin). Экономические издержки этого направления самые высокие, но если стоит задача сделать максимально адаптированное приложение для каждой из платформ, то это определенно лучший вариант.
Кроссплатформенный подход. Главная ценность этого подхода - ориентирование на Android и iOS из одной и той же кодовой базы. Этот вариант позволяет выкатить на рынок готовый продукт в максимально короткие сроки. Однако существует некоторая особенность касательно функциональных возможностей в сравнении с нативным подходом, поскольку так или иначе такие приложения получают поддержку нативных фич с некоторым запозданием. Тем не менее, на сегодняшний день этот разрыв, если и существует, то никакого решающего значения при выборе подхода не несет, особенно в eCommerce направлении, поскольку существует огромное множество сторонних библиотек и плагинов, покрывающих любые функциональные потребности. Однозначно этот подход является самым экономически эффективным вкупе с минимальным сроком выхода на рынок. Именно эти факторы подталкивают к небывалому развитию фреймворков, которые играют на этом поле. Для нас выбор был очевидным!
Теперь, давайте разберемся, какой именно фреймворк взять на вооружение. Наша команда выбрала Flutter и вот почему:
Кривая обучения и порог вхождения в понимание платформы должен быть простым и быстрым - это на самом деле очень важный показатель, он говорит о зрелости и технологичности платформы.
Интеграция с популярными редакторами - крайне важно, чтобы было удобно не только писать код, но и производить отладку, просматривать дерево виджетов, анализировать утечки памяти. Flutter имеет отличную интеграцию с VSCode, который хорошо знаком разработчикам .NET.
Горячая перезагрузка (Hot reload) - еще одна особенность, которая реально экономит время и нервы разработчика, ускоряя процесс разработки. В нативной разработке ничего подобного нет.
Производительность - по этому показателю Flutter показывает отличные результаты. По заявлениям разработчиков, фреймворк поддерживает частоту до 60 кадров в секунду, это позволяет делать приложения по скорости близкими к нативным. Мы это проверили и провели собственные испытания производительности (подробнее об этом чуть ниже).
Производительность
Перед началом разработки мы решили исследовать производительность отрисовки графического интерфейса, поскольку в нашем приложении основные расходы ресурсов идут именно на эти цели. За основу мы взяли очень простую задачу - отрисовку с анимацией рекламного слайдера. Отслеживать будем потребление памяти, FPS, загрузку CPU с помощью инструмента GameBench.
Для тестирования был реализован один и тот же интерфейс нативно на Android, на React Native и на Flutter. Время теста в каждом случае было фиксировано. Для кэширования изображений использовались библиотеки на каждой платформе. Условия тестирования максимально одинаковые. Давайте посмотрим, что из этого вышло.
В каждом показателе на диаграмме (кроме FPS) лучше считается наименьший результат.
FPS - во всех случаях результат очень близкий, Flutter свои заявленные значения выдает стабильно. Визуально разницы конечно не заметно.
Memory - тут мы видим уже более наглядную разницу, Flutter потребляет вдвое больше памяти, чем нативная реализация. Однако еще хуже дела обстоят с React Native.
CPU - и снова React Native тут выглядит явным аутсайдером, причина тому - использование JS Bridge между JS и Native code.
Battery - конечно, нативная реализация лучше всего оптимизирована под систему Android, кроме того, из предыдущих результатов логичный вывод о потреблении энергии напрашивается сам по себе, и Flutter в этом показателе занимает заслуженное второе место.
Даже на таком простом тесте мы видим разницу в подходах, хотя повторюсь, что для eCommerce проекта производительность не играет решающей роли.
Из альтернатив нативной разработке - Flutter по итогу является предпочтительным вариантом.
Архитектура приложения
Когда вы планируете разработку сложных приложений, то правильный выбор архитектуры имеет решающую роль при дальнейшем развитии проекта, его масштабируемости и в конце концов позволяет грамотно структурировать код, что делает работу команды над таким проектом более прозрачной и слаженной.
Хорошая архитектура должна пронизывать весь проект, помогая справиться с его сложностью, но никак не усложнять код надуманными логическими уровнями или чрезмерным обобщением. Иначе процесс разработки будет очень трудоемким, а сопровождение кода будет почти невозможным.
Архитектура нашего приложения построена по общепринятым стандартам, описанным в документации Android. Но поскольку она не учитывает специфики Flutter, нам необходимо модифицировать ее.
Давайте рассмотрим ее более детально. По сути, она состоит из трех уровней.
Поскольку в проекте используется система управления состоянием Riverpod, архитектура была расширена и выглядит так:
Как видите, она во многом повторяет каноничную архитектуру Android, но в ней немного перераспределены уровни. Рассмотрим подробнее изменения применяемые в нашей архитектуре.
Domain Layer - на этом уровне описаны доменные модели, которые могут быть изменены сервисами на уровне приложений (Application Layer), и они же заполняются репозиториями на уровне данных (Data Layer). Важно отметить, что эти модели должны описывать объекты бизнес логики, с которыми вы работаете через репозитории.
Application layer - по своему предназначению он схож с Domain Layer из Android архитектуры. На этом уровне сосредоточены классы-сервисы описывающие методы, которые будут использовать контроллеры. Это очень сильно помогает в том случае, когда одной и той же логикой управляет несколько виджетов или логика зависит от нескольких репозиториев. Таким образом, мы получаем более лучшее разделение задач. Стоит заметить, что прикладной уровень не нужно использовать повсеместно, контроллеры могут запрашивать данные и напрямую из репозиториев. Если сервисы просто перенаправляют вызовы методов из контроллеров в репозиторий - то прикладной уровень избыточен.
Клиент API (интеграция с Web API)
Когда речь заходит о взаимодействия с API, то всегда возникает вопрос о реализации клиента доступа и поддержанию его в актуальном состоянии. Но все становится чрезвычайно просто, если при создании API вы позаботились о том, чтобы оно было описано по спецификации OpenAPI 3. Это открывает возможность хранить все описание вашего API в схеме формата json. И как следствие - позволяет генерировать клиента в нужном нам языке, используя это формальное описание с помощью инструмента OpenAPI Generator.
Поскольку мы пишем приложение на Flutter, то клиента нам предстоит генерировать на языке Dart при помощи dart-dio генератора (Documentation for the dart-dio Generator).
Когда клиент будет готов, он будет сгенерирован в виде пакета, остается только указать в приложении, что мы будем его использовать. Таким образом, мы быстро получили все методы поддерживаемые API. Остается лишь “научить” репозитории работать с этими методами.
В дальнейшем, когда методы API поменяют сигнатуру, или произойдут иные структурные изменения, вам достаточно будет просто перестроить клиента заново. Как видите, тут все очень просто!
Безопасность
Для защиты всех передаваемых данных в приложении используется “токен на предъявителя” (Bearer Token). Токен выдается только авторизованному пользователю и далее используется при обмене запросами между клиентом (мобильным приложением) и сервером.
Токен на стороне сервера можно отозвать, после чего все клиенты вынуждены будут пройти процедуру авторизации повторно и получить новый токен.
Возникает логичный вопрос - как будет храниться токен на стороне клиента? Ведь использование токена не требует от предъявителя доказательства владения, и тут очень важно держать эту информацию под контролем. Для этих целей во Flutter будем использовать защищенное локальное хранилище.
Локальное хранилище служит для сохранения данных пользователей до тех пор, пока приложение не будет удалено. Но для разных платформ используются разные технологии:
для Android используется шифрование AES. Секретный ключ зашифрован с помощью RSA алгоритма, ключ от которого хранится в KeyStore.
для IOS используется KeyChain (связка ключей) - это безопасное хранилище, используемое для доступа к криптографическим ключам приложения.
Таким образом, на любой платформе используются лучшие практики безопасности, делая токен защиты недоступным для чужих глаз. Эти меры безопасности помогают сохранить доверие пользователей к продукту, что мы считаем очень важным.
UI/UX
В основе интерфейса всегда лежит концепция, пронизывающая все приложение, которая является фундаментом для построения удобного и функционального дизайна. В основе нашего приложения лежит система Material Design 3 или как ее принято называть - Material You.
На сегодняшний день это самая передовая версия системы дизайна от Google. В ней переосмыслены визуализации компонентов, появились новые цвета и новая анимация - все это уже доступно для использования во Flutter.
Вместе с тем Material Design предлагает системные (эталонные) токены для хранения значений стилей, типографики и анимации. Это открывает перед нами возможность использования одних и тех же значений стиля в файлах дизайна и коде.
Наша задача - сделать приложение так, чтобы все его компоненты выглядели одинаково в любом месте использования и самое главное - реализовать единое управление стилями этих компонентов. С этой задачей нам как раз помогают справиться токены дизайна (Design tokens). Давайте кратко посмотрим, как этот принцип работает.
Все, что нам нужно для создания Design Tokens, это Material Theme Builder. С его помощью можно сгенерировать тему с цветами и стилями по умолчанию. А уже на ее основе создать свою пользовательскую тему.
Существует два способа указания цвета, на которых будет базироваться вся схема, обычно это цвета бренда:
указать базовые цвета вручную
использовать “динамический цвет” - цвета будут выбраны на основе предоставленного изображения (например логотипа компании).
После того как базовые цвета выбраны, на их основе будет сгенерирована вся палитра токенов согласно концепции Material You, но уже на основе ваших пожеланий. Это чрезвычайно просто!
Теперь появляется возможность управлять токенами для разных режимов приложения (Dark mode/Light mode), что делает пользовательский интерфейс невероятно гибким, и позволяет менять стиль всего приложения буквально на лету, простым и логичным способом.
Итак, какие преимущества мы получили от использования токенов дизайна?
Больше не нужно изучать руководства по дизайну конкретных платформ, достаточно загрузить токены и применять стили Material Design как в проектировании, так и в разработке. Это своего рода дизайн-документация вашего проекта.
возможность быстрого перестроения приложения под новый стиль основанный на Material Design
легкость поддержания и обновления стилей
Заключение
Если вы находитесь в процессе создания мобильного eCommerce приложения или подумываете над началом разработки, мы надеемся, что это руководство будет вам полезно.
Мы постарались описать наш путь разработки мобильного приложения “крупными мазками”, останавливаясь лишь на наиболее крупных и важных этапах. Узнать больше о нашем проекте вы можете посетив наш репозиторий на GitHub.