SPM (Swift Package Manager) — встроенный менеджер пакетов экосистемы Swift, разработанный Apple для автоматизации подключения, сборки и обновления сторонних библиотек. SPM входит в состав компилятора Swift начиная с версии 3.0 (2016 год) и не требует отдельной установки. В отличие от CocoaPods и Carthage, SPM интегрирован напрямую с компилятором и Xcode, что делает его стандартным инструментом управления зависимостями в современных Swift-проектах. В статье разберём устройство Package.swift, команды SPM, написание собственных пакетов и миграцию с альтернативных менеджеров.
Главное
SPM (Swift Package Manager) — официальный менеджер пакетов для языка Swift, встроенный в компилятор swiftc и среду разработки Xcode. Он позволяет разработчикам подключать сторонние библиотеки, управлять их версиями и публиковать собственные пакеты. SPM впервые появился в Swift 3.0 (сентябрь 2016) как инструмент командной строки, а начиная с Xcode 11 (2019) получил полную интеграцию с графическим интерфейсом — теперь зависимости добавляются через меню File → Add Packages.
SPM автоматически загружает исходный код зависимостей из Git-репозиториев, собирает их параллельно с основным проектом и кеширует результаты, чтобы повторные сборки выполнялись быстрее. В отличие от CocoaPods, SPM не генерирует отдельный workspace (xcworkspace) — зависимости становятся частью основного проекта Xcode. По данным опроса Swift.org Developer Survey (2024), SPM используют 67% iOS-разработчиков, что делает его самым популярным инструментом управления зависимостями в Swift-экосистеме.
SPM поддерживает три платформы: Apple (iOS, macOS, tvOS, watchOS, visionOS), Linux (Ubuntu, CentOS, Amazon Linux) и серверный Swift (Vapor, Kitura). На Linux SPM работает полностью через командную строку без Xcode.
SPM построен вокруг трёх ключевых концепций: пакеты (packages), продукты (products) и цели (targets). Пакет — это Git-репозиторий с манифестом Package.swift. Продукт — результат сборки (библиотека или исполняемый файл). Цель — модуль внутри пакета, который компилируется в единицу сборки.
Когда разработчик добавляет зависимость в Package.swift, SPM выполняет следующие шаги:
~Library/Caches/org.swift.swiftpm/.Файл Package.resolved фиксирует точные версии всех зависимостей, чтобы команда разработчиков работала с идентичным набором библиотек. Этот файл должен быть добавлен в систему контроля версий (git).
Ключевое преимущество SPM перед аналогами — отсутствие централизованного реестра. Пакеты могут располагаться в любом публичном Git-репозитории: GitHub, GitLab, Bitbucket, а также на собственных Git-серверах компании. С версии Swift 5.2 SPM поддерживает двоичные зависимости (binary targets) — закрытые библиотеки, распространяемые через XCFramework без предоставления исходного кода.
Package.swift — это Swift-файл, который описывает структуру пакета и его зависимости. Файл пишется на самом Swift (не JSON, не YAML), что даёт возможность использовать условную логику, вычисляемые константы и функции внутри манифеста.
Базовая структура Package.swift:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyLibrary",
platforms: [
.iOS(.v16),
.macOS(.v13)
],
products: [
.library(
name: "MyLibrary",
targets: ["MyLibrary"]
),
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git",
from: "5.9.0"),
.package(url: "https://github.com/onevcat/Kingfisher.git",
from: "7.12.0"),
],
targets: [
.target(
name: "MyLibrary",
dependencies: [
"Alamofire",
"Kingfisher"
]
),
.testTarget(
name: "MyLibraryTests",
dependencies: ["MyLibrary"]
),
]
)
Разберём ключевые элементы:
// swift-tools-version: 5.9 — директива, указывающая версию SPM; от неё зависит доступный синтаксис манифеста.name — имя пакета, отображаемое в Xcode и используемое в ссылках зависимостей.platforms — минимальные версии платформ; SPM не позволит собрать пакет на более старой версии ОС.products — что «экспортирует» пакет: библиотеку (.library) или исполняемый файл (.executable).dependencies — список внешних пакетов с URL и версией; поддерживаются from:, exact:, branch:, revision:.targets — цели сборки; каждая цель содержит список зависимостей, ресурсы и swift-файлы из соответствующей директории (Sources/TargetName/).Пример указания точной версии, ветки и коммита:
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git",
exact: "1.17.3"),
.package(url: "https://github.com/pointfreeco/swift-composable-architecture.git",
branch: "main"),
.package(url: "https://github.com/apple/swift-log.git",
revision: "e5c6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4"),
]
С версии Swift 5.9 в Package.swift добавлена поддержка static/framework и linkerSettings, что позволяет точнее настраивать линковку для статических и динамических библиотек.
Swift Package Manager предоставляет набор команд для работы через терминал. Команды запускаются из корневой директории пакета (там, где лежит Package.swift).
# Создать новый пакет с библиотекой
swift package init --type library
# Создать исполняемый пакет (консольное приложение)
swift package init --type executable
# Собрать проект
swift build
# Собрать в релизной конфигурации
swift build -c release
# Запустить тесты
swift test
# Запустить конкретный тест
swift test --filter "MyLibraryTests/testExample"
# Загрузить и разрешить зависимости
swift package resolve
# Обновить зависимости до последних доступных версий
swift package update
# Показать граф зависимостей
swift package show-dependencies
# Очистить кеш сборки
swift package clean
# Сгенерировать Xcode-проект (до Xcode 11)
swift package generate-xcodeproj
При работе внутри Xcode большинство этих команд выполняются автоматически: зависимости разрешаются при открытии проекта, сборка запускается по ⌘B, тесты — по ⌘U. Однако знание терминальных команд необходимо для CI/CD-пайплайнов (GitHub Actions, GitLab CI, Jenkins), где Xcode недоступен.
Команда swift package resolve создаёт или обновляет файл Package.resolved. Этот файл фиксирует точные версии всех зависимостей, включая транзитивные, и должен быть добавлен в git. Рекомендуется запускать swift package update перед каждой новой feature-веткой, чтобы работать с актуальными версиями библиотек.
Создание собственного SPM-пакета полезно для инкапсуляции бизнес-логики в многомодульных проектах и для публикации open-source библиотек. Рассмотрим пошаговый процесс.
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library
Команда swift package init создаёт следующую структуру:
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│ └── MyNetworkKit/
│ └── MyNetworkKit.swift
└── Tests/
└── MyNetworkKitTests/
└── MyNetworkKitTests.swift
SPM автоматически сканирует директории Sources/ и Tests/: каждая поддиректория внутри Sources соответствует цели (target).
Добавим зависимости и настроим целевые платформы:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyNetworkKit",
platforms: [
.iOS(.v15),
.macOS(.v12)
],
products: [
.library(
name: "MyNetworkKit",
targets: ["MyNetworkKit"]
),
],
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git",
from: "5.9.0"),
],
targets: [
.target(
name: "MyNetworkKit",
dependencies: ["Alamofire"]
),
.testTarget(
name: "MyNetworkKitTests",
dependencies: ["MyNetworkKit"]
),
]
)
// Sources/MyNetworkKit/MyNetworkKit.swift
import Foundation
import Alamofire
public struct NetworkClient {
private let session: Session
public init() {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
self.session = Session(configuration: configuration)
}
public func fetchData(from url: String) async throws -> Data {
let response = try await session.request(url).serializingData().value
return response
}
}
Пушните пакет в Git-репозиторий и создайте SemVer-тег:
git init
git add .
git commit -m "Initial commit: MyNetworkKit"
git remote add origin https://github.com/username/MyNetworkKit.git
git push -u origin main
git tag 1.0.0
git push --tags
После этого любой разработчик сможет подключить ваш пакет через .package(url: "https://github.com/username/MyNetworkKit.git", from: "1.0.0").
Alamofire — самый популярный HTTP-клиент для Swift. Добавим его через SPM и выполним GET-запрос.
import Alamofire
func fetchUsers() {
AF.request("https://jsonplaceholder.typicode.com/users")
.validate()
.responseDecodable(of: [User].self) { response in
switch response.result {
case .success(let users):
print("Получено (users.count) пользователей")
case .failure(let error):
print("Ошибка: (error.localizedDescription)")
}
}
}
Библиотека Swinject предоставляет DI-контейнер для Swift. Подключается через .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0").
import Swinject
let container = Container()
container.register(NetworkServiceProtocol.self) { _ in NetworkService() }
container.register(DataRepositoryProtocol.self) { r in
DataRepository(networkService: r.resolve(NetworkServiceProtocol.self)!)
}
let repository = container.resolve(DataRepositoryProtocol.self)
repository?.loadData()
Пакет swift-log от Apple — единый API логирования, поддерживающий множество бэкендов (OSLog, console, файлы).
import Logging
var logger = Logger(label: "com.myapp.network")
logger.logLevel = .debug
logger.info("Сетевой запрос начат", metadata: [
"url": "(requestURL)",
"method": "GET"
])
logger.warning("Время ответа превысило 2 секунды")
logger.error("Ошибка соединения: нет интернета")
Эти три примера покрывают типовые сценарии использования SPM: HTTP-клиенты, DI-контейнеры и системная инфраструктура. Выбор библиотек не случаен — Alamofire, Swinject и swift-log входят в топ-20 самых звёздных Swift-пакетов на GitHub.
Если проект использует CocoaPods или Carthage, миграция на SPM выполняется в несколько шагов. Процесс безопасен: SPM-зависимости могут сосуществовать с CocoaPods и Carthage в одном проекте, что позволяет мигрировать постепенно.
.xcworkspace, откройте .xcodeproj и выполните Clean Build Folder.rm -rf Carthage/ в терминале.На момент 2025 года SPM поддерживает подавляющее большинство популярных Swift-библиотек. Исключения составляют некоторые ObjC-фреймворки без модульных карт (modulemap). Если библиотека ещё не поддерживает SPM — проверьте раздел Installation в её README; большинство авторов уже добавили поддержку SPM в последних версиях.
Часто задаваемые вопросы
SPM встроен в компилятор Swift и Xcode, не требует установки через gem или Homebrew. CocoaPods использует централизованный реестр Specs и генерирует отдельный workspace. Carthage работает через фреймворки без интеграции с проектом. SPM — единственный менеджер, интегрированный на уровне компилятора: зависимости разрешаются, кешируются и собираются параллельно с основным кодом.
Да, SPM поддерживает смешанные проекты Swift + Objective-C. ObjC-файлы внутри SPM-пакета автоматически попадают в Umbrella Header при условии корректного modulemap. Однако SPM не поддерживает статические библиотеки ObjC, не имеющие модульной карты. Рекомендуется подключать ObjC-библиотеки через SPM только если они предоставляют modulemap или написаны на чистом C.
SPM использует семантическое версионирование (SemVer). Если пакет A требует Alamofire 5.8+, а пакет B — Alamofire 5.9+, SPM выберет версию 5.9.x, удовлетворяющую обоим. Если конфликт неразрешим (один пакет требует 5.x, другой — 6.x), SPM сообщит об ошибке. В таком случае необходимо обновить один из пакетов или переключить зависимость на версию, совместимую с обоими требованиями.
На macOS: ~Library/Caches/org.swift.swiftpm/ и ~/Library/Developer/Xcode/DerivedData/. На Linux: ~cache/swiftpm/. При сборке SPM кеширует исходный код и скомпилированные объектные файлы. Для полной очистки кеша выполните swift package reset — эта команда удаляет кеш зависимостей и DerivedData для текущего проекта.
Да, начиная с Swift 5.2 SPM поддерживает двоичные цели (binary targets). Закрытая библиотека поставляется как XCFramework, а в Package.swift указывается путь к .xcframework. Исходный код при этом не раскрывается. Binary target указывается через .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework"). Это позволяет подключать коммерческие SDK, не нарушая лицензионных соглашений.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.