[Разработка под iOS, Разработка мобильных приложений, Swift] Как мы стартовали Vivid Money для iOS

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
02-Дек-2020 17:32


Всем привет! Меня зовут Илья. Я - iOS техлид в Vivid Money. Мы больше года занимались разработкой нашего финтех-продукта и теперь готовы поделиться с сообществом приобретенным опытом и знаниями.
Это вступительная статья, в которой я поверхностно затрону несколько технических решений, которые мы сделали на старте, а позже будут опубликованы статьи с детальным разбором самых интересных из них.АрхитектураДля начала мы определились с архитектурой проекта. Я имею ввиду не только архитектуру экранов/модулей, но и все остальные архитектурные решения. Конечно, рассказать обо всех из них в этом разделе не получится, поэтому затронем только архитектуру модулей, экранов и инъекцию зависимостей.Архитектура проектаТак как предполагалось, что проект будет большим и поделенным на продукты, мы решили разделить его на несколько модулей. Это помогает лучше структурировать код, а также облегчает разработку в продуктовых командах. Архитектура модулей выглядит так:
На схеме присутствует 4 слоя:
  • Core. Это проекты, которые либо не связаны с приложением и могут быть использованы где угодно, либо те, от которых зависят все вышестоящие слои.
  • Platform. В этом слое 2 проекта. DesignKit содержит все, что связано с UI приложения: от цветов и шрифтов до готовых компонентов или даже экранов. Platform служит основной для всех фича-проектов и основного приложения. Там содержатся сервисы, общие экраны, конфигурации, сущности и так далее.
  • Features. Проекты, в которых разрабатываются отдельные фичи или целые продукты. Эти проекты не связаны между собой, что позволяет быстрее их разрабатывать, проще тестировать и не смешивать код из разных продуктов.
  • App. Объединяет все проекты воедино.
Архитектура экрановМы решили подобрать что-то, что удовлетворяло бы нашим потребностям и не содержало ничего лишнего. В итоге мы пришли к VIP (View, Interactor, Presenter). Мы сохранили основы VIPER, но убрали Router и Entity. Такое разделение модуля помогает лучше его протестировать. Также разделение пригодилось в некоторых местах, где потребовалось использовать разные реализации view или interactor (да, это не миф).Router мы заменили на Coordinator. Это хороший паттерн, который позволяет сделать модули независимыми друг от друга и сосредоточить всю логику переходов в рамках одной user story внутри одного класса.Инъекция зависимостейМы не используем библиотеку для инъекции зависимостей. На этом хотелось бы закончить, но, кажется, надо дать этому небольшое объяснение.Во-первых, мы не очень любим подключать сторонние фреймворки, особенно те, которые можно легко заменить своим решением. Во-вторых, мы не нашли практической пользы от фреймворков для DI.В нашем проекте инъекция зависимостей проста и состоит из двух частей:
  • Класс Container, который содержит все зависимости, объявленные в виде переменных. Этот класс расширяется в каждом проекте и закрывается протоколом. Если, например, нужно получить зависимость из модуля Platform, нужно написать следующий код: let d = (Container.shared() as PlatformContainer).dependency Если не писать код в одной строке, он будет выглядеть чуточку лучше.
  • Класс Assembly, который внедряет зависимости в каждый конкретный модуль (то есть собирает его). Этот класс использует Container для получения зависимостей. Все зависимости в компонентах VIP модуля - это force-unwrapped переменные, значения которым и присваивает Assembly. В остальные классы зависимости внедряются через инициализатор.
Управление сторонними зависимостямиПо части управления зависимостями у нас все было как в большинстве проектов – мы использовали CocoaPods. Во-первых, это уже проверенный менеджер зависимостей; во-вторых, его поддерживают почти все open-source библиотеки.Как бы мы этого ни хотели, но спустя некоторое время у нас появилось относительно много зависимостей, без которых мы бы не могли обойтись (Firebase, Amplitude и прочие похожие фреймворки), и их постоянная пересборка занимала много времени (конкретное время сложно посчитать из-за постоянно меняющейся кодовой базы и самих зависимостей). Тогда мы решили попробовать Carthage.На первой итерации мы сделали симбиоз из этих двух менеджеров, так как не все библиотеки поддерживали Carthage. Но через время полностью перешли на Carthage и вдобавок внедрили Rome - утилиту для кэширования собранных библиотек.Также были попытки использовать SPM, но на тот момент был ряд проблем, которые не позволили это сделать: отсутствие поддержки кастомных конфигураций сборки, проблемы с обновлением библиотеки при изменении ее версии, отсутствие поддержки SPM некоторыми библиотеками. Но мы надеемся на то, что проблемы будут решены, и мы сможем полностью перейти на этот менеджер зависимостей.ТестированиеВ самом начале разработки мы мечтали об отсутствии ручных тестировщиков, что позволило бы нам сделать короткие релизные циклы и избавиться от человеческого фактора при проверке функционала. К сожалению, были обстоятельства, не позволившие добиться этого на старте, но мы уверенно движемся в этом направлении. В любом случае, нам надо было подумать над организацией тестирования продукта. В итоге мы пришли к тому, что будем писать и Unit, и UI тесты.В Unit тестах мы используем фреймворк SwiftyMocky, который помогает генерировать моки для типов и предоставляет множество полезных функций для тестирования. По уже устоявшейся парадигме мы тестируем, используя структуру Given-When-Then, чтобы все тесты выглядели однотипно и были логически структурированы.Unit тесты в основном пишутся на общие компоненты (утилиты, сервисы, и т.д.) и классы со сложной бизнес-логикой (в большинстве случаев это Presenter и Interactor), которую будет достаточно проблематично проверить в UI тестах.UI тесты у нас появились значительно позже. В них мы тоже не стали выдумывать ничего особенного и сделали несколько вспомогательных классов для реализации паттерна Page object.UI тесты в нашем проекте делятся на 2 типа: компонентные и end-to-end. Компонентные тесты проверяют работу отдельного экрана или его части с использованием моков, а end-to-end тесты проверяют некоторую цепочку экранов и используют реальное API, но на dev контуре.Также в качестве теста мы внедрили snapshot тесты, но говорить об их пользе пока рано. В идеале хотелось бы использовать их для тестирования компонентов из дизайн системы.Генерация API клиентовНаш бэкенд разделен на микросервисы, из чего следует, что и в приложении мы обращаемся к нескольким API. Следить за каждым и обновлять код вручную – слишком трудозатратная задача, и мы решили это автоматизировать.Каждый микросервис имеет swagger спецификацию, с помощью которой мы генерируем фреймворки, используя Swagger Codegen. Мы немного изменили шаблоны для генерации, чтобы они удовлетворяли нашим требованиям, и автоматизировали процесс обновления фреймворка в CI.Каждый сгенерированный API клиент находится в отдельном репозитории и добавлен в проект с помощью Carthage.По части генерации API клиентов есть улучшения, которые можно сделать, но такой подход уже дал огромный прирост к скорости разработки и избавил от необходимости ручного обновления API.Код-стандартНам нужно было сделать некоторые шаги к тому, чтобы код был понятен всем в любом месте приложения и разрабатывался быстро. Таких шагов было сделано несколько.Самый базовый - это написание code conventions. Там сосредоточены все правила и рекомендации по синтаксису. Почти все из них проверяются с помощью SwiftLint, а для некоторых написаны кастомные правила. Соглашения по стилистике кода помогают нам быстрее проводить код-ревью и делают код проще для чтения.Далее были описаны:
  • Правила работы с репозиторием: как называть ветки, как писать сообщения к коммитам и так далее;
  • Процесс выполнения задачи: какие задачи можно брать, приоритеты задач, статусы задач, как создать пул-реквест;
  • Используемые паттерны и механизмы: как решать типовые задачи (кэшировать данные, создавать сервисы и тому подобное);
  • Терминология: типичные названия методов или бизнес-определений в коде.
Все это помогает нам разрабатывать одинаково и не заниматься решением проблем, которые уже решены.Также мы используем Danger CI для проверки пул-реквестов. Список наших правил на данный момент небольшой: проверка на заполнение нужных полей в пул-реквесте, проверка на количество внесенных изменений, поиск TODO, замечания от Swiftlint и пара рекомендательных сообщений. Это помогает не упускать в них важных деталей и напоминать о вещах, о которых можно легко забыть.АвтоматизацияВ проекте с большой кодовой базой и растущим числом разработчиков не обойтись без автоматизации. Поэтому верным решением будет уделить этому внимание в самом начале проекта.Мы написали несколько своих скриптов, которые автоматизируют работу:
  • Скрипт для генерации VIP модулей, который ускоряет разработку экранов.
  • Скрипт для генерации фича-проектов.
  • Скрипты для скачивания локализации, фича-тогглов, удаленной конфигурации и некоторых ресурсов, нужных для приложения. Про этот пункт можно рассказать немного подробнее. Дело в том, что мы скачиваем свежие версии всех этих ресурсов в момент запуска приложения, но на случай проблем с загрузкой мы предусмотрели вариант дефолтной версии ресурсов, которая близка к настоящей. И, чтобы каждый раз не скачивать все эти файлы вручную, мы написали эти скрипты, которые запускаются перед сборкой приложения на CI.
Во избежание merge-конфликтов в файлах проекта мы используем XcodeGen, который генерирует файлы проекта по yaml спецификациям.С появлением такого количества скриптов встал вопрос удобства использования, ведь запоминать их названия, аргументы и порядок вызова кажется не самой интересной задачей. Поэтому мы создали скрипт, который вызывает остальные скрипты. Цель его работы проста - привести проект в актуальное состояние. В процессе работы он скачивает ресурсы, обновляет зависимости, генерирует файлы проекта и моки, настраивает схемы. Сначала мы не вызывали этот скрипт руками, а сделали его вызов автоматическим на каждый merge, pull или checkout. Но так как скрипт выполнялся некоторое время, это приводило к постоянным ожиданиям, хотя после, например, checkout в новую ветку ничего в проекте не поменялось. Поэтому на данный момент он вызывается вручную, но если бы получилось значительно сократить время его работы, то мы бы вернулись к автоматическому вызову.Также мы создали приложение для Mac OS, которое предоставляет интерфейс для вызова всех наших скриптов. Оно было очень простым и мало востребованным, но в данный момент мы его перерабатываем, чтобы упростить работу с проектом.Настройка проектаТак как проект у нас не самый простой в настройке, а требующий некоторых утилит и вызовов различных скриптов, мы решили упростить его настройку.Сначала это был исполняемый файл, который надо было запустить, чтобы скачались все зависимости (ruby, brew, python, и так далее) и выполнились необходимые настройки. Но позже мы реализовали настройку проекта через Ansible. Это позволило нам держать все в одном месте и настраивать не только компьютеры сотрудников, но и билд-агенты.Подводя итогиМы рассказали коротко о тех вещах и том опыте, который мы накопили, когда только начали делать первые шаги в проекте.Мы планируем и дальше делиться своим опытом разработки, так как считаем это важной частью развития комьюнити. Чтобы мы делали это лучше, пишите комментарии и конструктивную критику. Будем очень признательны.Всем спасибо!
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_razrabotka_pod_ios (Разработка под iOS), #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_swift, #_ios, #_arhitektura (архитектура), #_testirovanie (тестирование), #_startap (стартап), #_avtomatizatsija (автоматизация), #_blog_kompanii_vivid_money (
Блог компании Vivid Money
)
, #_razrabotka_pod_ios (
Разработка под iOS
)
, #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
)
, #_swift
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 18:49
Часовой пояс: UTC + 5