Монорепозиторий с Swift: организация кода для начинающих

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Монорепозиторий с Swift: организация кода для начинающих

Когда вы начинаете разрабатывать приложения на Swift для iOS или macOS, рано или поздно перед вами встаёт вопрос: как организовать код, чтобы он не превратился в хаос? Особенно если у вас несколько проектов (например, iOS-приложение, watchOS-расширение и серверная часть на Vapor). Один из современных подходов — монорепозиторий (monorepo). В этой статье мы разберём, что такое монорепозиторий, зачем он нужен в Swift-разработке и как его организовать на практике.

Что такое монорепозиторий и зачем он нужен?

Монорепозиторий (monorepo) — это подход к управлению кодом, при котором все проекты, библиотеки и модули хранятся в одном репозитории (например, в Git). В отличие от мультирепозиториев, где каждый проект живёт в своём отдельном хранилище, монорепозиторий объединяет всё в одном месте.

Для Swift-разработчиков это даёт несколько преимуществ:

  • Единая версия зависимостей — вы точно знаете, какая версия библиотеки используется во всех проектах.
  • Упрощённый рефакторинг — изменение API в одной библиотеке сразу видно во всех зависимых проектах.
  • Общие настройки — один SwiftLint, один CI/CD конфиг, одни правила форматирования.
  • Лёгкий поиск — всё в одном месте, не нужно переключаться между репозиториями.

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

Структура монорепозитория для Swift-проектов

Предположим, вы создаёте приложение для iOS с общими модулями. Типичная структура монорепозитория на Swift может выглядеть так:

MyMonorepo/
├── Apps/
│   ├── iOSApp/          # Основное iOS-приложение
│   │   ├── Sources/
│   │   ├── Resources/
│   │   └── Package.swift
│   └── macOSApp/        # macOS-версия
│       ├── Sources/
│       └── Package.swift
├── Packages/
│   ├── Core/            # Общие утилиты (сети, логирование)
│   │   ├── Sources/
│   │   └── Package.swift
│   ├── UIComponents/    # Переиспользуемые UI-компоненты
│   │   ├── Sources/
│   │   └── Package.swift
│   └── Analytics/       # Модуль аналитики
│       ├── Sources/
│       └── Package.swift
├── Scripts/             # Вспомогательные скрипты
├── .swiftlint.yml       # Единый конфиг линтера
├── Package.swift        # Корневой манифест (опционально)
└── README.md

Обратите внимание: здесь используется Swift Package Manager (SPM) — стандартный инструмент для управления зависимостями в Swift. Каждый модуль — это отдельный Swift Package, который можно подключать к приложениям.

Как создать монорепозиторий на Swift: пошаговое руководство

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

Шаг 1: Создайте корневую директорию и инициализируйте Git

mkdir MyMonorepo
cd MyMonorepo
git init

Шаг 2: Создайте структуру папок

mkdir -p Apps/iOSApp/Sources
mkdir -p Packages/Core/Sources
mkdir -p Packages/UIComponents/Sources

Шаг 3: Создайте Package.swift для модуля Core

Создайте файл Packages/Core/Package.swift:

// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "Core",
    platforms: [
        .iOS(.v16),
        .macOS(.v13)
    ],
    products: [
        .library(
            name: "Core",
            targets: ["Core"])
    ],
    targets: [
        .target(
            name: "Core",
            path: "Sources")
    ]
)

Шаг 4: Напишите простой код в модуле Core

Создайте Packages/Core/Sources/Logger.swift:

import Foundation

public struct Logger {
    public static func log(_ message: String) {
        print("[Core] \(message)")
    }
    
    public init() {}
}

Шаг 5: Создайте Package.swift для UIComponents

Аналогично создайте Packages/UIComponents/Package.swift:

// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "UIComponents",
    platforms: [
        .iOS(.v16)
    ],
    products: [
        .library(
            name: "UIComponents",
            targets: ["UIComponents"])
    ],
    dependencies: [
        .package(path: "../Core")
    ],
    targets: [
        .target(
            name: "UIComponents",
            dependencies: ["Core"],
            path: "Sources")
    ]
)

Шаг 6: Создайте простой UI-компонент

Создайте Packages/UIComponents/Sources/MyButton.swift:

import SwiftUI
import Core

public struct MyButton: View {
    let title: String
    let action: () -> Void

    public init(title: String, action: @escaping () -> Void) {
        self.title = title
        self.action = action
    }

    public var body: some View {
        Button(action: {
            Logger.log("Button '\(title)' tapped")
            action()
        }) {
            Text(title)
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(8)
        }
    }
}

Шаг 7: Создайте iOS-приложение, которое использует модули

Создайте Apps/iOSApp/Package.swift:

// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "iOSApp",
    platforms: [
        .iOS(.v16)
    ],
    dependencies: [
        .package(path: "../../Packages/Core"),
        .package(path: "../../Packages/UIComponents")
    ],
    targets: [
        .executableTarget(
            name: "iOSApp",
            dependencies: [
                "Core",
                "UIComponents"
            ],
            path: "Sources")
    ]
)

Шаг 8: Напишите код приложения

Блоги

Рекомендаций по книгам