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 не генерира отделно работно пространство (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
# Изграждане в release конфигурация
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 за логване, поддържащ множество backend (OSLog, конзола, файлове).
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 frameworks без модулни карти (modulemap). Ако библиотека все още не поддържа SPM — проверете секцията Installation в нейния README; повечето автори вече са добавили поддръжка на SPM в най-новите версии.
Често задавани въпроси
SPM е вграден в компилатора на Swift и Xcode, не изисква инсталация чрез gem или Homebrew. CocoaPods използва централизиран регистър Specs и генерира отделно работно пространство. Carthage работи чрез frameworks без интеграция с проекта. 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също