Все статьи

Организация раздельных репозиториев KMM проекта

Мобильное приложение13 февраля 2026

Способы организации репозитория KMM проекта

При работе с KMM (Kotlin Multiplatform Mobile) проектом можно использовать два подхода:

  1. Монорепозиторий - один общий репозиторий, где находятся и общий код, и платформенные модули для Android и iOS.
  2. Раздельные репозитории - общий код и платформенные модули вынесены в отдельные репозитории. Такой подход упрощает управление зависимостями и доступом.

Для раздельной организации репозиториев можно выделить следующие способы организации:

  • Общий модуль размещается в отдельном репозитории, iOS и Android при этом подключают общий модуль как стороннюю библиотеку.
  • Общий модуль размещается в репозитории Android проекта, при этом в Android проекте он используется как модуль, а в iOS как внешняя зависимость.

Для нашего проекта мы выбрали реализацию раздельного репозитория общий модуль при этом будет включен в Android проект.

Плюсы такого подхода:

  • Изоляция репозиториев: Процессы разработки двух параллельных проектов не пересекаются. Реквесты находятся в разных репозиториях, что упрощает контроль задач и код ревью. Невозможность изменения кода, написанного другой командой.
  • Изоляция кода: Приложения можно разрабатывать и тестировать отдельно, что упрощает поддержку и снижает вероятность ошибок.
  • Независимость разработки: Команды могут работать с разными версиями общего модуля без ожидания обновлений от других команд разработки.
  • Доступность общего модуля Android команде, что позволяет ускорить разработку в небольших командах, за счет исключения отдельного проекта под общий модуль.

Минусы:

  • Сложность настройки: В сравнении с моно репой, требуется больше действий для реализации.
  • Дополнительная синхронизации: При изменении общего модуля необходимо обновлять фреймворк для iOS проекта. При этом сборка фреймворка для iOS возможна только с устройств под управлением MacOS.
  • Необходимость настройки версионности общего модуля: в iOS проекте необходимо явно указывать версию общего модуля, иначе возможны конфликты последней версии общего модуля и iOS проекта.

Сборка iOS фреймворка

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

Для начала определяем таргеты для сборки фреймворка:

Затем регистрируем гредл таски для сборки фреймворка и сохранения сгенерированных файлов фреймворка в директории под контролем версий для дальнейшего доступа к фреймворку из репозитория.

При вызове таски iosFramework происходит сборка фреймворка и обновление его файлов в дирректории ./iOSFramework/

Настройка .podspec для подключения фреймворка через поды в ios проекте

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

В корне репозитория с sahred модулем создается файл shared.podspec

В файле необходимо заполнить поля в соответсвии с проектом:

  • spec.name - название 
  • spec.vendored_framework - путь к фреймаорку из корня проекта
  • spec.ios.deployment_target - необходимая версия iOS

Настройка доступа к приватному репозиторию с фреймворком при обновлении подов

Для подключения общего модуля в iOS проекте добавляем ссылку на репозиторий, используя :git =>””

При обновлении подов из приватного репозитория требуется авторизация. Чтобы избежать необходимости повторной авторизации при каждом обновлении, можно настроить доступ по SSH.

Использование ci для автоматизации и оптимизации сборки фреймворка

Сборка фреймворков под ios возможна только на устройствах под управлением macOS, по этой причине нельзя гарантировать сборку shared модуля для ios всеми участниками андройд команды и, как следствие, возникает трудность в поддержке актуальной сборки фреймворка. Решением может стать обновление фреймворка средствами CI, при этом сборка должна происходить на дежурном устройстве с macOS на борту. Мы используем GitLab для наших репозиториев и по этой причине был использован Gitlab CI.

Первым делом необходимо подготовить дежурное устройство, для этого на устройстве с macOS необходимо установить:

  • Xcode (необходим для компиляции фреймворка).
  • Homebrew (упрощает установку дополнительных утилит).
  • Java:

Устанавливаем GitLab Runner:

Зарегистрируем раннер в вашем проекте:

В ходе регистрации нужно указать:

  • URL GitLab (например, https://gitlab.com или адрес локального сервера).
  • Токен регистрации, который можно найти в Settings > CI/CD > Runners вашего проекта.
  • Тип раннера: shell.

Проверяем, что раннер зарегистрирован:

Создаем файл .gitlab-ci.yml в корне Android проекта:

Этот пайплайн запустится на раннере с тэгом macos только после обновления ветки develop. В начале работы скрипта git переключается на ветку, которая будет содержать скомпилированный фреймворк. Затем происходит слияние develop в текущую ветку, чтобы исключить конфликты. Далее происходит сборка фреймворка вызовом gradle таски assembleIosFramework, и фиксирование полученных артефактов в коммите с последующим пушем.

Так как фреймворк располагается только в ветке “shared_develop” в iOS проекте необходимо в Podfile явно указать ветку зависимости через параметр :branch.

Настройка версионности пода

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

Эта проблема исключается, если каждая ревизия ios проекта будет ссылаться на определенную версию общего модуля. Так как под хранится в git репозитории, невозможно использовать параметр :version в Podfile, но возможно использовать параметр :tag, в котором будет записана версия собранного фреймворка.

Чтобы возможно было указать тег для пода, необходимо его установить в репозитории:

Осуществляем коммит актуальной сборки

Устанавливаем тэг и пушим изменения

После этого в подфайле можем подтянуть зависимость по этому тегу с помощью параметра :tag =>

Для автоматизации версионирования, добавляем его в CI. При каждом обновлении фреймворка будет рассчитана его версия и записана в git tag.

Новый номер версии рассчитывается на основе параметра spec.version файла shared.podspec. Для этого берется значение этого поля из файла, раскладывается на Мажорную, минорную и патч версию. патч версия инкрементируется, после чего номер версии снова собирается в строку. Новый номер версии обновляется в shared.podspec и в конце работы скрипта записывается в git tag.

При таком алгоритме для увеличения мажорной или минорной версии можно вручную изменить параметр spec.version в shared.podspec. 

 

Особенности подключения пода mokoMvvmSwiftUi.

В нашем проекте мы используем библиотеку Moko-MVVM. Для возможности работы с Flow в  SwiftUI удобно использовать библиотеку mokoMvvmSwiftUi, подключаемая в iOS проект. Для того, чтобы эта библиотека могла работать название фреймворка должно быть “MultiPlatformLibrary”, а флаг isStatic равен false.

В Podfile дополнительно указываем зависимость mokoMvvmSwiftUi

0