Привет! На связи команда Joy Dev. Представим ситуацию, вы работаете над проектом, дела идут хорошо, проект растет, и в какой‑то момент вас в команде становится все больше и больше! Проект вырастает, и становится много модулей и фич, но появились проблемы:
из‑за большого объема модулей все сложнее ориентироваться в проекте (не говоря уже о том, чтобы погрузиться в него с нуля);
фичи не разделены, а это значит, что перенести или заменить какой‑либо модуль становится долгим и болезненным делом;
проект собирается мучительно долго;
от мысли о конфликтах в файле проекта *.pbxcodeproj бросает в дрожь.
А может, стоило уже на старте проекта предусмотреть, что нужно позаботиться о модульной структуре и выбрать подходящую организацию кода? Давайте рассмотрим три способа разделения проектов на модули в XCode, плюсы и минусы каждого подхода, и выберем наиболее удобный.
Development Pods
Сабмодуль на отдельном репозитории
Подпроект
Почему мы решили прибегнуть к модульности
На одном из наших проектов в один момент стало огромное количество кода:
по верстке UI компонентов, который переиспользовался по всему проекту, но редко модифицировался;
утилиты и расширения к стандартным сущностям;
слой по работе с сетью и локальному хранению данных;
существенный объем legacy кода, который подлежал рефакторингу. Однако было трудно организовать работу нескольких разработчиков над кодом из‑за постоянных конфликтов.
Кроме того, у проекта были большие перспективы на масштабирование, поэтому нужно было оптимизировать работу и сократить время сборки приложения.
Лучше всего помогает решить эти проблемы именно модульность проекта.
Модуль — это логически и функционально завершенный компонент, который имеет минимально возможное количество зависимостей от других модулей и скрывает в себе реализацию функциональности. Модульность позволяет систематизировать структуру проекта и наладить удобное взаимодействие для большой команды.
Development Pods
Development Pods или по‑простому «девподы» — это полезный и удобный инструмент. Девподы — модули с символическими ссылками, при изменении которых изменяются оригинальные файлы.
Как создать девпод в проекте?
1. Создаем папку модуля.
Pod::Spec.new do |s|
s.name = ‘MyModule’
s.version = ‘1.0’
s.summary = 'My Module Summary'
s.homepage = ‘Some link’
s.author = { ‘name’ => ‘name@joy-dev.com’ }
s.source = { :path => "." }
s.platform = :iOS, ’13.0’
s.swift_version = ‘5.0’
s.source_files = ‘MyClasses/*’
end
3. Включаем наш новоиспеченный модуль в основной Podfile проекта.
pod "MyModule", :path => "./MyModulesFolder/MyModule"
4. Выполняем установку подов и в Pods проекте видим папку Development Pods.
5. Для использования модуля в основном проекте достаточно включить его в файл import MyModule.
Преимущества Dev Pods
для большой команды: при такой модульной структуре «толкания локтями» становится гораздо меньше, конфликты в файле проекта становятся редкостью, разрабатывать отдельные фичи можно независимо и безболезненно;
для оптимизации времени сборки: Xcode не будет пересобирать девпод, если в нем не вносилось никаких изменений;
для визуального разбиения кода на модули: больше не придется перекапывать 20 папок в поисках того самого экрана.
Недостатки Dev Pods
придется изменить файловую структуру проекта: все компоненты необходимо сложить в одну папку;
не поддерживают версионность;
необходимо менять модификаторы доступа у всех публичных сущностей методов extension на public.
Сабмодуль на отдельном репозитории
Удобное решение при затягивании отдельного независимого модуля или работы двух команд над проектом. Пара простых примеров, когда это полезно: модуль со всеми UI‑компонентами и стилями, которые используются в верстке (дизайн‑система) или модуль с сетевым слоем. Такой сабмодуль затягивается, как отдельный под по ссылке на репозиторий с указанием версии.
При таком подходе отдельная команда может проводить работы над кодом, при этом изменения не коснутся основного проекта, пока мы не поднимем номер версии в Podfile.
Имхо, самое лучшее в таком подходе — это возможность управлять версиями подов, накатывать новые изменения или откатываться до предыдущих.
Итого, если выносим отдельные файлы (набор UI‑компонентов)/модуль работы с API — это очень удобно.
Указываем в подфайле зависимость одним из способов:
source “https://github.com/nfme/JoyDevDesignSystem.git”
pod 'JoyDevDesignSystem', '0.0.1'
pod “JoyDevDesignSystem”,:git => “https://github.com/name/JoyDevDesignSystem.git”,:tag => '0.0.1'
Плюсы Сабмодуля
не нужно беспокоиться о внешних зависимостях;
можно версионировать: удобно откатывать изменения до нужной версии.
Если хотим вынести, к примеру, «Экран каталога товаров», то здесь проявляются минусы такого подхода.
более сложное вынесение модуля из‑за необходимости создавать гит‑проект;
усложняется процесс‑ревью: придется ревьюить по модулям (например, при создании нового экрана, отдельно ревью UI, отдельно моделей и сервисов);
работа с несколькими репозиториями в рамках одной задачи;
нет оптимизации по времени — файлы будут билдиться вместе со всем проектом.
Подпроект
Это отличный вариант для тех, кто планирует создавать несколько проектов на базе одного модуля или имеет возможность генерировать модули из конфигурационных файлов.
К примеру, есть устойчивый независимый сетевой слой, на базе которого планируется сделать несколько приложений со схожим функционалом, но различным дизайном и бизнес‑логикой. Чтобы не переносить файлы из проекта в проект, просто создаем независимый проект с завершенным функционалом и, когда нужно, добавляем его в свое рабочее пространство.
Такой подход к модульности отлично сочетается с использованием утилит для генерации проектов — например, XcodeGen. Если затянуть себе такую утилиту, то можно просто и быстро генерировать подпроекты из конфиг‑файлов (yml, например) и вовсе забыть о конфликтах в файле проекта.
Как создать подпроект
Создаем новый проект.
Помещаем его в папку проекта.
Получаем следующую структуру.
Добавляем зависимость в основной модуль.
Преимущества подпроектов
pbxproj‑файл разбивается на несколько, т. е. конфликтов в файле проекта сразу меньше, соответственно, разные команды могут одновременно работать над разными подпроектами;
модули реализованы наглядно;
автоматически определяется принадлежность файлов;
легко установить внешние зависимости прямо в подфайле (каждый подпроект будет иметь зависимости от своих внешних библиотек).
Недостатки подпроектов
нужно полностью изменить структуру проекта;
необходимо менять модификаторы доступа у всех сущностей методов extension на public.
Что выбирать
Мы выбирали по исходным данным и целям своего проекта. Поскольку у нас была одна команда, мы планировали одновременную работу над несколькими модулями, у нас не было острой необходимости в версионности, и было удобно разбить проект на большое количество мелких модулей, в том числе по экранам, мы выбрали первый способ — разделение на девподы.
Кстати, с момента выбора этого решения прошел год, и мы точно можем сказать, что на данный момент это самый удобный вариант.