Довольно часто разработчики сталкиваются с необходимостью провести оптимизацию локального хранилища приложения. Она может включать в себя множество факторов: от замены одной из библиотек до изменения всей архитектуры проекта. В этой статье мы покажем на своём примере, что необходимо учитывать и как действовать, получив подобный заказ, и как улучшить производительность e-commerce приложения.

Что имеем? Заказчик пришёл к нам с задачей провести анализ архитектуры своего мобильного приложения и оптимизировать его. Приложение было создано для помощи продавцам, консультантам и экспертам, чтобы они могли быстрее и качественнее взаимодействовать с покупателями. Оно позволяет сотрудникам просматривать доступные в сети магазинов товары, проверять их наличие на складах и в других магазинах, добавлять товары в корзину и на ее основе оформлять заказы с возможностью самовывоза и доставки курьером. Заказчик хотел улучшить производительность e-commerce приложения и повысить эффективность работы сотрудников.

Какой план работы?

Мы разделили работу наших специалистов на несколько этапов для удобства и полного покрытия поставленной задачи.

  1. Анализ архитектуры приложения, выявление основных проблем в UX.
  2. Определение способов организации БД приложения.
  3. Анализ функционирования приложения.
  4. Этап оптимизации.

Анализ архитектуры приложения

В процессе аналитики мы выяснили, что информация в мобильном приложении является полностью динамической (обновляемой). Приложение содержит в себе информацию о доступных в сети магазинов товарах, их наличии на складах и в других магазинах, и бонусах пользователя в локальной бонусной программе. Для получения необходимой информации реализовано взаимодействие с сервером. Авторизация пользователя происходит через интегрированное внешнее локальное приложение компании заказчика.

В случае, если пользователю нужны какие-то конкретные выборки товаров, он может воспользоваться обширной системой фильтров приложения. Фильтры являются автоматически обновляемыми через сервер в зависимости от входных параметров запроса пользователя. В ответ приходят данные о товарах в формате JSON, происходит их обработка и хранение. Затем производится постраничный вывод информации пользователю с возможностью изменения отображения списка товаров.

Когда требуется авторизация или регистрация клиента магазина в приложении для оформления заказа или просмотра бонусов, также происходит обмен данными с сервером.

как улучшить производительность e-commerce приложения
DFD-диаграмма с декомпозицией основного процесса e-commerce приложения

Проблемы на этапе разработки

В ходе разработки мобильного приложения возникали проблемы с реализацией некоторых элементов. Это повлекло за собой ограничения в удобстве использования приложением и возможности расширения его функционала. Например, планировалось, что приложению не нужна своя база данных и данные будут приходить от сервера. В приложении же был реализован только временный кэш. Оказалось, что в некоторых случаях продавцы часто выходили из приложения, кэш сбрасывался и приходилось снова ждать, пока пройдут «тяжелые» запросы с большим количеством данных. В других же случаях продавцы наоборот подолгу пользовались приложением, однако из-за того, что кэш являлся временным, он сбрасывался и им снова приходилось ждать запросы на большие данные.

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

Стоит также упомянуть об отсутствии тестирования в процессе разработки. Рекомендуется писать и использовать тесты, учитывающее ожидаемое поведение функционала. Иными словами, прибегать к подходу разработки через тестирование (TDD). Его отсутствие влечёт за собой снижение надёжности приложения.

Из-за этих проблем у заказчика и возникла необходимость пересмотра изначального подхода к архитектуре проекта и рефакторинга, чтобы улучшить производительность e-commerce приложения.

Увеличение структуры приложения

Предполагалось, что все данные будут храниться на сервере и, так как они обновляются достаточно часто, не было бы необходимости хранить их на устройстве. Поэтому решили использовать временный кэш без локальной базы данных. Архитектура MVC идеально подходила под такие условия. Поскольку все данные обрабатываются динамически, нет необходимости в слое управления и хранения данных. Также не было необходимости в слое взаимодействия контроллеров друг с другом. Количество экранов и последовательных переходов между ними было ограничено.

Однако в ходе реализации и поддержки приложения структура проекта сильно разрослась. Появилось большое количество нового функционала, который был реализован в новых экранах приложения. Появилось несколько новых вкладок, которые также содержали в себе объем переходов и взаимодействия между компонентами, не уступающий изначальной реализации проекта. Появилась необходимость не только последовательного перехода между контроллерами, но и возможность возврата в предыдущие состояния приложения и взаимодействия между отдельными вкладками программы. Изначальная архитектура с этим уже не справлялась.

В ходе эксплуатации выяснилось, что функционал кэширования является недостаточным для быстрого использования приложения. Из-за этого возникали жалобы от персонала на медленную скорость работы приложения и постоянные подгрузки.

Таким образом, было решено изменить архитектуру приложения и добавить в него локальную базу данных.

Способы организации локальной базы данных e-commerce приложения

В большинстве мобильных приложений основная информация, выводимая пользователю, хранится удаленно на сервере. Приложение же запрашивает эти данные посредством API.

Для мобильного приложения помощи продавцам-консультантам также есть свое API. Однако, так как данных много, они хранятся на разных серверах и для получения разных данных необходим механизм синхронизации разных групп запросов. Таким механизмом в приложении являются справочники.

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

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

Для текущего e-commerce приложения использовали кеширование по требованию. В приложении запрос на каждый товар является уникальным и существует огромное количество всевозможных фильтраций и других запросов для отображения полной информации о товарах. Поэтому заранее предугадать какая информация понадобится пользователю невозможно.

Для реализации кэша в приложении был использован NSCache. Его реализация является простой и не требует никаких дополнительных настроек.

Однако приложение начало постепенно разрастаться и появилась необходимость хранить довольно большое количество данных. Также появились запросы на данные, которые обновляются очень редко. Поэтому было решено использовать локальную базу данных вместо кэша.

Локальные базы данных могут хранить довольно большой объем информации, и они не очищаются при выключении приложения. Это даст возможность оптимизировать распределение ресурсов памяти в приложении, а также не запрашивать большое количество не обновляемой информации.

Наиболее популярными вариантами реализации локальной базы являются Realm и Core Data.

Преимущества Core Data:

  • доступность «из коробки», что позволяет не увеличивать объем приложения;
  • фреймворк является достаточно стабильным, так как, по сути, является базовым для любого из приложений;
  • скорость выполнения текстового поиска и поиска на большом объеме данных;
  • хранилище является одним из стандартных форматов данных.

Преимущества Realm:

  • лаконичная запись и простота использования;
  • поддержка шифрования и синхронизации данных;
  • скорость работы на относительно небольших объемах данных.

Было решено использовать Realm. Он является более простым в реализации, что положительно скажется на удобстве его дальнейшей поддержки. Также обладает оптимизацией работы с данными, что позволит ускорить работу и производительность e-commerce приложения.

Анализ функционирования e-commerce приложения

Новая архитектура должна была предполагать возможность работы с локальным хранилищем. Также необходимо было оставить ключевые особенности реализации приложения. Например, в проекте используется Swinject – легковесный фреймворк для внедрения зависимостей в языке Swift. Для реализации архитектуры приложения был использован паттерн Clean architecture. Его имплементация тоже должна была сохраниться в проекте.

Безусловно, новая архитектура не должна была кардинально отличаться от предыдущей, так как сроки на реинжиниринг и рефакторинг были ограничены. Еще необходимо было учесть время на регрессионное тестирование, которое не должно было выявить большое количество правок по ранее реализованному функционалу.

Для реализации новой архитектуры проекта были выбраны такие альтернативы, как MVC, MVP, MVVM-C и VIPER. Каждая из архитектур предлагает свои решения поставленных задач и обладает рядом характеристик, по которым их можно сравнить. Для выбора оптимального варианта мы применили вариантный анализ. Он заключается в попарном сравнении решений по их критериям, а также в сравнении важности критериев между собой. По итогам применения вариантного анализа наиболее оптимальным решением оказалась архитектура MVVM-C. Эта архитектура является достаточно простой в поддержке и реализации за счет опыта разработчиков проекта. Дополнительная модификация в виде координатора позволила решить проблему с маршрутизация пользовательских переходов между экранами. Также архитектура обладает хорошим разделением обязанностей между ее модулями, что повышает их тестируемость.

Оптимизация и улучшенная производительность e-commerce приложения

Realm поддерживает систему миграций, которая позволяет пользователям обновлять базу данных в случае ее изменения. Поэтому при каждом создании конфигурации базы данных надо обязательно указывать ее версию. Также, если есть такая необходимость, можно указать как именно будет обновляться база данных, условия ее обновления и выбирать версию до которой надо обновиться в зависимости от условий. Все изменения базы данных Realm проходят внутри write транзакции, поэтому был написан блок, который упрощает запись данных в базу, осуществляя необходимые для этого проверки.

Как улучшить производительность e-commerce приложения

Для записи в базу данных Realm, объект должен являться наследником базового объекта Object и содержать поле primaryKey. Для объектов, у которых нет изначально поля primaryKey, был создан базовый класс, который его создает.

Как улучшить производительность e-commerce приложения

Локальное хранилище приложения позволило существенно сократить скорость запуска программы. Теперь она составляет примерно 5 секунд вместо 12. Также это позволило избавиться от повторной загрузки ранее загруженных данных во время работы приложения, что ускорило его работу в целом.

Способ делегирования

Для реализации новой архитектуры приложения был использован способ делегирования. Делегат – это ссылка на экземпляр другого класса, который может взять на себя часть обязанностей по отклику на определенную ситуацию. Однако есть существенная проблема в таком подходе. Делегирование подразумевает удерживание ссылки на объект одного класса другим. Из-за этого в приложении часто возникают утечки памяти, которые бывает затруднительно отследить.

Решением проблемы делегирования и оптимизации работы приложения благодаря избавлению от утечек памяти, могут стать биндинги. Биндинги не предполагают удерживания одних объектов другими. Они работают по принципу подписки объекта на какое-то событие и, если этот объект был освобожден из памяти, его подписка будет удалена вместе с ним. Для реализации биндингов в приложении мы использовали ReactorKit. ReactorKit – это платформа для реактивной и однонаправленной архитектуры приложений Swift. Это комбинация программирования Flux и реактивного программирования. Действия пользователя и состояния просмотра доставляются на каждый уровень через наблюдаемые потоки. Эти потоки однонаправлены. Представление может генерировать только действия, а реактор – только состояния.

Как улучшить производительность e-commerce приложения

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

Новая архитектура благоприятно сказалась на распределении обязанностей между модулями приложения, что в свою очередь благоприятно сказалось на тестируемости. Теперь каждый модуль архитектуры отвечает принципу единой ответственности, а тяжелая логика по маршрутизации переходов была перенесена в отдельные координаторы.

Также была произведена оптимизация скорости отклика приложения и скорости взаимодействия пользователя с тяжеловесными таблицами. Это улучшение было достигнуто благодаря переходу от делегирования к биндингам, что позволило избавиться от утечек памяти в некоторых местах приложения.

Результат

В итоге нашей команде удалось выполнить все требования клиента и значительно улучшить производительность e-commerce приложения. Отдельным аспектом для улучшения является обеспечение безопасности приложения. Мы написали об этом специальный гайд, где подробно описали все шаги по оптимизации. А если у вас есть идея проекта, пишите нам и мы обсудим с вами все детали проекта!