JavaRush /Курсы /Swift SELF /Что такое SwiftPM и зачем Package.swift

Что такое SwiftPM и зачем Package.swift

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

1. Основные понятия SwiftPM

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

Тут и появляется SwiftPM (Swift Package Manager). Это не «ещё одна страшная штука», а просто стандартный способ Swift-проекту договориться с компьютером, как он должен собираться и запускаться. Причём не «как получится», а по описанию, которое лежит рядом с кодом.

Небольшой факт для уверенности: Swift обычно поставляется вместе со SwiftPM как с частью набора инструментов (toolchain). То есть это не редкий плагин из тёмного угла интернета, а базовый инструмент экосистемы.

SwiftPM: сборка и зависимости

Если кратко, SwiftPM — это штука, которая умеет две большие вещи: собирать ваш проект и подключать зависимости (библиотеки). И делает это не «по настроению», а на основе манифеста.

Важно понять одну мысль: SwiftPM — это не IDE. Он не рисует кнопки, не раскрашивает код и не спорит с вами за отступы. Он работает как строгий, но справедливый повар: ему дают рецепт — он готовит. Не дали рецепт — он стоит и молча смотрит, как вы страдаете.

Вот упрощённая схема, что вообще происходит:

flowchart TD
    A["Вы (разработчик)<br/>пишете код"] --> B["SwiftPM читает Package.swift<br/>и понимает, что собирать"]
    B --> C["Компиляция исходников<br/>в нужном порядке"]
    C --> D["Результат: исполняемый файл<br/>или библиотека"]

И ключевой элемент этой схемы — Package.swift.

Пакет: проект, который «умеет объяснить себя»

Слово package сначала сбивает с толку: «пакет… это как посылка?». На практике пакет — это просто директория проекта, в корне которой лежит файл-описание сборки. Этот файл и есть Package.swift.

Можно думать так: папка без Package.swift — это просто папка с .swift файлами. Папка с Package.swift — это проект, который умеет сказать SwiftPM: «вот что у меня внутри, вот как меня собирать».

Swift.org в самом простом гайде для CLI прямо называет Package.swift манифестом и показывает, что при создании проекта SwiftPM генерирует структуру, где рядом с Package.swift появляется Sources/ (а позже обычно и Tests/). Мы подробно разберём структуру папок в следующей лекции дня, а сегодня нам важно понять роль манифеста.

Два мира: сборка и приложение

Когда вы запускаете команды SwiftPM (мы детально сделаем это в следующих лекциях дня), первым делом SwiftPM ищет Package.swift и читает его. Важно понимать: ваш проект начинается не с main.swift, а с того, что сборщик понимает структуру.

Удобно представить это как два разных мира:

Мир Вопрос Главный файл
Мир сборки «Что собирать и как?»
Package.swift
Мир приложения «Что делать при запуске?»
Sources/...

Swift.org формулирует эту идею очень прямо: Package.swift хранит метаданные и зависимости проекта, а исходники приложения живут в Sources/... (и entry point — там же). Сегодня мы не углубляемся в точку входа и структуру папок, но важно увидеть: манифест — это «про сборку», исходники — это «про поведение программы».

3. Файл Package.swift: что внутри и как его читать

Package.swift — про сборку, а не про логику приложения

С виду Package.swift выглядит как обычный Swift-код. Там действительно пишется Swift, там можно заводить let, можно собирать строки, можно даже делать маленькие вычисления. И вот здесь новичок попадает в ловушку: «О, значит я могу написать в Package.swift логику приложения!»

Нет. Технически — вы можете написать что угодно, но смысл файла другой: Package.swift — это декларация того, как собирать проект. Это не место для бизнес-логики, не место для print("Привет"), не место для парсинга команд и хранения данных. Он должен быть коротким, скучным и предсказуемым. Как инструкция к микроволновке: интересно — ноль, зато работает каждый раз.

Внутри Package.swift мы обычно описываем:

  • имя пакета;
  • какие цели (targets) в нём есть (например, исполняемая программа);
  • (иногда) зависимости от внешних пакетов.

Чтобы описывать всё это, в манифесте импортируется специальный модуль PackageDescription. Он даёт типы вроде Package, через которые мы «собираем описание проекта».

// swift-tools-version: 6.2: версия правил, по которым читают манифест

Когда вы видите вверху манифеста строку вида:

// swift-tools-version: 6.2

она кажется чем-то декоративным, как лавровый лист в супе: вроде лежит, но зачем — непонятно. На самом деле это очень практичная штука: она говорит SwiftPM, какая минимальная версия инструментов нужна, чтобы правильно понять ваш Package.swift.

Можно воспринимать это как «версия языка манифеста». Манифест со временем менялся: добавлялись новые возможности, менялись правила, появлялись новые API в PackageDescription. Поэтому SwiftPM должен понимать: «по каким правилам читать этот файл».

В рамках курса мы фиксируем Swift 6.2, поэтому в примерах будет // swift-tools-version: 6.2. Старайтесь не менять это без причины: это часть контракта между проектом и инструментами сборки.

import PackageDescription: зачем он нужен

До этого вы уже видели import Foundation и привыкли: «импорт — чтобы получить доступ к API». В Package.swift тот же принцип, но контекст другой: мы импортируем модуль PackageDescription, потому что именно он предоставляет типы, которыми описывается пакет.

То есть это не «подключили библиотеку для строк/дат», а «подключили словарь терминов для сборки».

Минимальный, абсолютно нормальный каркас манифеста выглядит так:


// swift-tools-version: 6.2
import PackageDescription

let package = Package(
    name: "LibraryCLI"
)

Здесь важно не «что он делает», а «что он означает»: мы создали объект Package, который описывает пакет с именем LibraryCLI.

4. Цели и нейминг: как не запутаться в именах

Фиксируем имя LibraryCLI: меньше ошибок, больше предсказуемости

На этом дне курса мы сознательно «упрощаем вселенную» и договариваемся: одно имя — везде. Это звучит скучно, но это прямо спасает новичков от вечного вопроса: «почему swift run не запускает то, что я думаю?».

Мы фиксируем:

Сущность Что это Имя в курсе
package name имя пакета в
Package.swift
LibraryCLI
executable target name имя исполняемой цели (программы)
LibraryCLI
бинарник/CLI-команда имя запускаемой команды
LibraryCLI

Почему это полезно? Потому что без этой дисциплины легко получить ситуацию: пакет называется Library, цель называется CLI, а запускать надо swift run BooksApp, и вы сидите как человек, который открыл три одинаковые двери и не помнит, где его квартира.

Исполняемая цель: .executableTarget(...)

Когда пакет должен собираться в программу, SwiftPM должен понимать, что это именно исполняемая цель, а не библиотека. Современный SwiftPM позволяет объявлять это явно через .executableTarget(...) (и это появилось как отдельная явная возможность, чтобы цель не приходилось «угадывать по имени файла main.swift» и чтобы лучше работало с @main).

Минимальный манифест с исполняемой целью выглядит так:

// swift-tools-version: 6.2
import PackageDescription

let package = Package(
    name: "LibraryCLI",
    targets: [
        .executableTarget(name: "LibraryCLI")
    ]
)

Обратите внимание на стиль: мы не «настраиваем миллион параметров», а задаём только самое важное. Чем меньше магии — тем меньше сюрпризов.

«Можно я сделаю переменную?» — можно, но аккуратно

Иногда хочется избежать дублирования строк. Например, имя LibraryCLI встречается и в name, и в .executableTarget. И тут появляется нормальная идея: «а сделаю let name = ...».

Это окей, потому что это не усложняет манифест и не превращает его в мини-программу. Главное правило — чтобы по Package.swift можно было пробежать глазами и понять проект.

// swift-tools-version: 6.2
import PackageDescription

let name = "LibraryCLI"

let package = Package(
    name: name,
    targets: [
        .executableTarget(name: name)
    ]
)

Если вы когда-нибудь ловили баг «в одном месте переименовал, в другом забыл» — вы уже понимаете, почему такая константа делает жизнь спокойнее.

5. Мини-пример: скелет LibraryCLI

Соберём мысль в одну картинку. Представьте, что у нас в проекте есть два ключевых элемента: манифест и исходники. Сейчас нам достаточно даже не реальных файлов, а мысленной карты (в следующей лекции дня мы сделаем это «по-настоящему»).

import Foundation

let layout = """
Package.swift
Sources/
  LibraryCLI/
    main.swift
"""
print(layout)
// Package.swift
// Sources/
//   LibraryCLI/
//     main.swift

И вот что важно: SwiftPM не угадывает ваш проект по вдохновению. Он смотрит на Package.swift и говорит: «Ага, package LibraryCLI, есть исполняемая цель LibraryCLI — значит буду искать исходники цели там, где принято».

6. Типичные ошибки со SwiftPM и Package.swift

Ошибка №1: писать логику приложения в Package.swift.
Обычно это происходит из лучших побуждений: «раз это Swift — значит тут можно делать всё». Но результат печальный: манифест превращается в сложную программу, которую трудно читать, трудно менять и легко сломать при обновлении инструментов. Правильная привычка — держать Package.swift коротким, описательным и скучным: имя, цели, (иногда) зависимости.

Ошибка №2: путать «проект», «пакет», «таргет» и «бинарник».
Новичок часто думает, что это всё одно и то же. На самом деле это четыре близких, но разных сущности. Чтобы не утонуть в терминах, мы на этом дне фиксируем единое имя LibraryCLI для ключевых сущностей. Это не «единственно правильный стиль в индустрии», это учебная страховка от путаницы.

Ошибка №3: менять // swift-tools-version: 6.2 «наугад».
Иногда кажется: «поставлю версию повыше, вдруг станет лучше». Но // swift-tools-version — это не ускоритель, а контракт совместимости. Если поставить версию, которую ваши инструменты не поддерживают, SwiftPM может вообще перестать читать манифест. В курсе мы используем Swift 6.2 — значит и в манифесте пишем 6.2, не устраивая лотерею.

Ошибка №4: забыть import PackageDescription и удивляться ошибкам.
Это типичная история: вы пишете Package(...), а компилятор манифеста говорит «не знаю такой тип». Потому что Package — не встроенное слово Swift, а тип из PackageDescription. Лечится одной строкой импорта — и привычкой помнить, что в манифесте мы используем специальный API.

Ошибка №5: устроить «зоопарк имён» и потом не понимать, что запускать.
Если пакет называется одним именем, исполняемая цель — другим, а бинарник в итоге — третьим, то команда swift run ... превращается в угадайку. Особенно обидно, когда всё собрано правильно, но вы запускаете «не то». Поэтому дисциплина нейминга — это не занудство, а способ быстрее учиться, пока мозг занят более важными вещами, чем поиск опечатки в имени.

1
Задача
Swift SELF, 47 уровень, 3 лекция
Недоступна
Скучный манифест
Скучный манифест
1
Задача
Swift SELF, 47 уровень, 3 лекция
Недоступна
Исполняемая цель
Исполняемая цель
1
Задача
Swift SELF, 47 уровень, 3 лекция
Недоступна
Единое имя
Единое имя
1
Задача
Swift SELF, 47 уровень, 3 лекция
Недоступна
Ремонт манифеста
Ремонт манифеста
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ