JavaRush /Курсы /Swift SELF /SwiftPM: swift package init, Sources/ и Tests/

SwiftPM: swift package init, Sources/ и Tests/

Swift SELF
47 уровень , 4 лекция
Открыта

1. SwiftPM и команда swift package init

Когда вы впервые слышите «менеджер пакетов», в голове легко возникает образ монстра, который требует жертвоприношения в виде YAML‑конфигов и танцев под бубен. SwiftPM в целом добрее: он старается жить по договорённости (конвенциям). То есть вы кладёте файлы в ожидаемые папки, а SwiftPM «сам понимает», что с ними делать — без миллиона ручных настроек.

Самое важное: SwiftPM управляет проектом как пакетом (package). Пакет — это просто директория, в корне которой лежит файл Package.swift. Этот файл называется манифестом: в нём описывается, что это за проект и как его собирать.

Команда swift package init — это «генератор скелета», который создаёт правильную структуру папок и несколько стартовых файлов, чтобы проект сразу был в адекватном состоянии.

Создаём проект: swift package init как «генератор скелета»

Начинать проще всего с правильной привычки: создаём отдельную папку под проект и выполняем команду изнутри этой папки. Это важно, потому что SwiftPM привязывает «имя пакета по умолчанию» к имени директории, а ещё ему нужно понимать, где корень проекта.

Вот типичный сценарий для нашего курса. Обратите внимание: мы фиксируем единый нейминг. Папка = LibraryCLI, package = LibraryCLI, target = LibraryCLI, бинарник (команда запуска) = LibraryCLI.

mkdir LibraryCLI
cd LibraryCLI

swift package init --type executable --name LibraryCLI

Иногда у новичков возникает вопрос: «А почему --type executable?». Потому что мы пишем консольную программу, то есть исполняемый файл. По умолчанию swift package init нередко создаёт библиотечный шаблон, и это неожиданно, если вы хотели «просто CLI и print("Hello")».

2. Что появилось на диске: Package.swift, Sources/, Tests/

После swift package init вы увидите несколько файлов и папок. И тут полезно поймать правильную ментальную модель: SwiftPM не «угадывает по настроению», а следует структуре проекта. Появляется Package.swift, а исходники лежат в Sources, тесты — в Tests.

Схематично (идеальная картинка курса выглядит так):

LibraryCLI/
├── Package.swift
├── Sources/
│   └── LibraryCLI/
│       ├── main.swift
│       ├── Book.swift
│       └── Library.swift
└── Tests/
    └── LibraryCLITests/
        └── LibraryCLITests.swift

Иногда SwiftPM создаёт Sources/main.swift (без подпапки LibraryCLI). Это не трагедия и не знак судьбы, что вы «не настоящий программист». Это просто вариант шаблона.

Для курса мы хотим единообразие: создайте папку Sources/LibraryCLI/ и перенесите туда main.swift. Дальше вам будет проще: имя папки совпадает с именем цели, и мозг не тратит энергию на «где я и почему это так называется».

Чтобы зафиксировать роли элементов, держите небольшую табличку:

Элемент Что это Зачем нужно
Package.swift
манифест пакета описывает сборку и структуру проекта
Sources/
исходный код то, что компилируется в продуктовый модуль
Tests/
тестовый код то, что запускается отдельной командой тестирования

И ещё один важный нюанс: для executable‑проекта точкой входа часто является файл main.swift, который SwiftPM воспринимает как «entry point».

4. Нейминг курса: почему везде LibraryCLI

Название — это не косметика. Название — это то, как вы разговариваете с инструментами. Когда вы пишете swift run LibraryCLI, вы буквально говорите: «Запусти цель (и бинарник), которая называется LibraryCLI».

Если сегодня вы назвали пакет LibraryCli, папку library, а бинарник вообще случайно стал App, то завтра вы будете тратить время не на Swift, а на археологию по терминалу.

SwiftPM по умолчанию часто берёт имя пакета из имени директории (и его можно изменить через --name). Нам это на руку: мы специально создаём папку LibraryCLI и этим же именем называем пакет.

Давайте проговорим «тройку» курса человеческим языком:

  • package = LibraryCLI — как называется проект на уровне SwiftPM
  • target = LibraryCLI — что конкретно собирается в программу
  • binary/CLI command = LibraryCLI — как называется исполняемый файл, который запускается

Да, это три разных понятия. Да, мы специально делаем их одинаковыми. Это как назвать кота «Кот», чтобы не путаться, кто из вас кто, когда вы разговариваете с ветеринаром.

5. Первые файлы приложения: main.swift, Book, Library

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

Без парсинга команд, без файлов, без сети — только то, что мы уже умеем: struct, массивы и print.

main.swift: дружелюбный старт

Положите файл сюда: Sources/LibraryCLI/main.swift. Минимальная версия может быть такой:

print("LibraryCLI запущен.")
print("Пока я умею только здороваться 🙂 (и да, это временно).")

// LibraryCLI запущен.
// Пока я умею только здороваться 🙂 (и да, это временно).

Обратите внимание: здесь нет import Foundation — он и не нужен, пока мы не используем Foundation‑типы. Это хороший стиль: импортируем только по необходимости.

Book.swift: первая доменная модель

Создайте Sources/LibraryCLI/Book.swift:

struct Book {
    let title: String
    let author: String
    let year: Int
}

Ничего сложного: struct хранит данные. Позже мы научим эту модель быть более «умной», но сейчас она как табличка на полке: «книга такая-то».

Library.swift: библиотека как список книг

Создайте Sources/LibraryCLI/Library.swift:

struct Library {
    private(set) var books: [Book] = []

    mutating func add(_ book: Book) {
        books.append(book)
    }
}

Здесь мы используем private(set), чтобы снаружи можно было читать books, но менять их — только через методы библиотеки. Это маленький шаг к аккуратному дизайну: «данные не должны течь во все стороны».

Используем модели в main.swift

Теперь вернёмся в Sources/LibraryCLI/main.swift и сделаем короткий сценарий:

var library = Library()

library.add(Book(title: "1984", author: "George Orwell", year: 1949))
library.add(Book(title: "Dune", author: "Frank Herbert", year: 1965))

print("Книг в библиотеке: \(library.books.count)") // Книг в библиотеке: 2

Заметьте важную вещь: main.swift «видит» Library и Book без дополнительных import. Это потому, что все эти файлы находятся в одной и той же цели (target) и компилируются как один модуль.

Именно ради этого мы так аккуратно складываем их в Sources/LibraryCLI/.

6. Проверяем, что SwiftPM видит файлы

На старте почти у всех бывает ощущение: «Я создал файл… но работает ли он вообще?». Это нормальная тревога: мозг программиста на 30% состоит из подозрения, что компьютер вас обманывает.

Хорошая новость: у SwiftPM есть очень чёткие правила, и если вы их соблюдаете, проект будет собираться предсказуемо.

Самая частая проверка — убедиться, что вы кладёте .swift‑файлы именно в Sources/LibraryCLI/, а не «рядом с Package.swift», не в Sources/ без папки, и не в какую-нибудь творческую директорию source_code_final_FINAL2. SwiftPM ожидает стандартную структуру, и именно это упрощает жизнь: меньше настроек, меньше загадок.

Вторая полезная проверка — смотреть на структуру проекта как на контракт:

flowchart TD
    A["Вы создаёте файл .swift"] --> B{"Файл лежит в Sources/LibraryCLI/ ?"}
    B -- "да" --> C["SwiftPM компилирует файл в цель LibraryCLI"]
    B -- "нет" --> D["SwiftPM может не увидеть файл или увидит не так, как вы ждёте"]
    C --> E["Код доступен из main.swift без import"]

И ещё один нюанс, который снижает хаос: старайтесь, чтобы имена совпадали с тем, как SwiftPM генерирует структуру. Так проще сверять свою структуру с ожидаемой и быстрее находить расхождения.

7. Типичные ошибки при работе со SwiftPM-проектом

Ошибка №1: запускать swift package init не там, где вы думаете.
Очень легко выполнить команду в домашней папке или в родительской директории, а потом удивляться, почему Package.swift появился «не в проекте». SwiftPM создаёт файлы в текущей директории, поэтому базовое правило простое: сначала cd, потом swift package init.

Ошибка №2: забыть --type executable и получить библиотеку вместо программы.
Если вы ожидали main.swift и запуск как CLI, а получили структуру библиотечного пакета, то вы просто создали не тот тип. В SwiftPM это норма: тип пакета можно выбрать опцией --type.

Ошибка №3: «у меня есть файл, но SwiftPM его как будто не компилирует».
Чаще всего причина в том, что файл лежит не в Sources/<TargetName>/. SwiftPM ожидает конвенционную структуру Sources/ и Tests/.
Лечится просто: переместите файл в правильную папку.

Ошибка №4: путаница в названиях (LibraryCLI, LibraryCli, librarycli).
На некоторых системах регистр может «прощаться», а на некоторых — нет. Даже если ОС не ругается, вы сами начнёте ошибаться в командах и путаться в путях. В курсе мы намеренно фиксируем одно имя LibraryCLI везде, чтобы это было скучно, предсказуемо и не ломалось.

Ошибка №5: пытаться писать код приложения в Package.swift.
Да, Package.swift — это Swift‑файл. Но его роль — описывать сборку, а не запускать вашу программу. Если вы начнёте писать там бизнес‑логику, вы очень быстро окажетесь в мире странных ошибок, потому что манифест выполняется в другом контексте и вообще не для этого предназначен.

Держите «код приложения» в Sources/, а Package.swift — как паспорт проекта.

1
Опрос
Слои ошибок, 47 уровень, 4 лекция
Недоступен
Слои ошибок
Архитектура ошибок в Swift
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ