SPM (Swift Package Manager) — wbudowany menedżer pakietów ekosystemu Swift, opracowany przez Apple do automatyzacji podłączania, budowania i aktualizowania bibliotek zewnętrznych. SPM wchodzi w skład kompilatora Swift od wersji 3.0 (2016 rok) i nie wymaga osobnej instalacji. W przeciwieństwie do CocoaPods i Carthage, SPM jest zintegrowany bezpośrednio z kompilatorem i Xcode, co czyni go standardowym narzędziem do zarządzania zależnościami w nowoczesnych projektach Swift. W artykule omówimy budowę Package.swift, polecenia SPM, tworzenie własnych pakietów i migrację z alternatywnych menedżerów.
Najważniejsze
SPM (Swift Package Manager) — oficjalny menedżer pakietów dla języka Swift, wbudowany w kompilator swiftc i środowisko programistyczne Xcode. Pozwala programistom podłączać biblioteki zewnętrzne, zarządzać ich wersjami i publikować własne pakiety. SPM pojawił się po raz pierwszy w Swift 3.0 (wrzesień 2016) jako narzędzie wiersza poleceń, a od Xcode 11 (2019) uzyskał pełną integrację z interfejsem graficznym — teraz zależności dodaje się przez menu File → Add Packages.
SPM automatycznie pobiera kod źródłowy zależności z repozytoriów Git, buduje je równolegle z głównym projektem i buforuje wyniki, aby kolejne kompilacje były szybsze. W przeciwieństwie do CocoaPods, SPM nie generuje osobnego workspace (xcworkspace) — zależności stają się częścią głównego projektu Xcode. Według ankiety Swift.org Developer Survey (2024), SPM używa 67% programistów iOS, co czyni go najpopularniejszym narzędziem do zarządzania zależnościami w ekosystemie Swift.
SPM obsługuje trzy platformy: Apple (iOS, macOS, tvOS, watchOS, visionOS), Linux (Ubuntu, CentOS, Amazon Linux) i serwerowy Swift (Vapor, Kitura). Na Linuxie SPM działa w pełni przez wiersz poleceń bez Xcode.
SPM jest zbudowany wokół trzech kluczowych koncepcji: pakiety (packages), produkty (products) i cele (targets). Pakiet to repozytorium Git z manifestem Package.swift. Produkt to wynik kompilacji (biblioteka lub plik wykonywalny). Cel to moduł wewnątrz pakietu, który jest kompilowany jako jednostka.
Gdy programista dodaje zależność w Package.swift, SPM wykonuje następujące kroki:
~Library/Caches/org.swift.swiftpm/.Plik Package.resolved ustala dokładne wersje wszystkich zależności, aby zespół programistów pracował z identycznym zestawem bibliotek. Ten plik powinien być dodany do systemu kontroli wersji (git).
Kluczową zaletą SPM w porównaniu z analogami jest brak scentralizowanego rejestru. Pakiety mogą znajdować się w dowolnym publicznym repozytorium Git: GitHub, GitLab, Bitbucket, a także na własnych serwerach Git firmy. Od wersji Swift 5.2 SPM obsługuje zależności binarne (binary targets) — zamknięte biblioteki dystrybuowane przez XCFramework bez udostępniania kodu źródłowego.
Package.swift — to plik Swift, który opisuje strukturę pakietu i jego zależności. Plik jest pisany w samym Swift (nie JSON, nie YAML), co umożliwia używanie logiki warunkowej, obliczanych stałych i funkcji wewnątrz manifestu.
Podstawowa struktura 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"]
),
]
)
Omówimy kluczowe elementy:
// swift-tools-version: 5.9 — dyrektywa wskazująca wersję SPM; od niej zależy dostępna składnia manifestu.name — nazwa pakietu, wyświetlana w Xcode i używana w odnośnikach zależności.platforms — minimalne wersje platform; SPM nie pozwoli zbudować pakietu na starszej wersji systemu.products — co pakiet „eksportuje": bibliotekę (.library) lub plik wykonywalny (.executable).dependencies — lista pakietów zewnętrznych z URL i wersją; obsługiwane są from:, exact:, branch:, revision:.targets — cele kompilacji; każdy cel zawiera listę zależności, zasoby i pliki swift z odpowiedniego katalogu (Sources/TargetName/).Przykład podania dokładnej wersji, gałęzi i commita:
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"),
]
Od wersji Swift 5.9 w Package.swift dodano obsługę static/framework i linkerSettings, co pozwala precyzyjniej konfigurować linkowanie dla bibliotek statycznych i dynamicznych.
Swift Package Manager udostępnia zestaw poleceń do pracy przez terminal. Polecenia uruchamia się z katalogu głównego pakietu (tam, gdzie znajduje się Package.swift).
# Utwórz nowy pakiet z biblioteką
swift package init --type library
# Utwórz pakiet wykonywalny (aplikacja konsolowa)
swift package init --type executable
# Zbuduj projekt
swift build
# Zbuduj w konfiguracji release
swift build -c release
# Uruchom testy
swift test
# Uruchom konkretny test
swift test --filter "MyLibraryTests/testExample"
# Pobierz i rozwiąż zależności
swift package resolve
# Aktualizuj zależności do najnowszych dostępnych wersji
swift package update
# Pokaż graf zależności
swift package show-dependencies
# Wyczyść cache kompilacji
swift package clean
# Wygeneruj projekt Xcode (przed Xcode 11)
swift package generate-xcodeproj
Podczas pracy w Xcode większość tych poleceń jest wykonywana automatycznie: zależności są rozwiązywane przy otwieraniu projektu, kompilacja uruchamiana przez ⌘B, testy przez ⌘U. Jednak znajomość poleceń terminalowych jest niezbędna w pipeline'ach CI/CD (GitHub Actions, GitLab CI, Jenkins), gdzie Xcode nie jest dostępny.
Polecenie swift package resolve tworzy lub aktualizuje plik Package.resolved. Ten plik ustala dokładne wersje wszystkich zależności, w tym tranzatywnych, i powinien być dodany do git. Zaleca się uruchamianie swift package update przed każdą nową gałęzią feature, aby pracować z aktualnymi wersjami bibliotek.
Tworzenie własnego pakietu SPM jest przydatne do enkapsulacji logiki biznesowej w projektach wielomodułowych oraz do publikowania bibliotek open-source. Omówimy proces krok po kroku.
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library
Polecenie swift package init tworzy następującą strukturę:
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│ └── MyNetworkKit/
│ └── MyNetworkKit.swift
└── Tests/
└── MyNetworkKitTests/
└── MyNetworkKitTests.swift
SPM automatycznie skanuje katalogi Sources/ i Tests/: każdy podkatalog wewnątrz Sources odpowiada celowi (target).
Dodamy zależności i skonfigurujemy platformy docelowe:
// 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
}
}
Wypchnij pakiet do repozytorium Git i utwórz tag 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
Po tym każdy programista będzie mógł podłączyć Twój pakiet przez .package(url: "https://github.com/username/MyNetworkKit.git", from: "1.0.0").
Alamofire — najpopularniejszy klient HTTP dla Swift. Dodamy go przez SPM i wykonamy zapytanie 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("Pobrano \(users.count) użytkowników")
case .failure(let error):
print("Błąd: \(error.localizedDescription)")
}
}
}
Biblioteka Swinject udostępnia kontener DI dla Swift. Podłączana przez .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()
Pakiet swift-log od Apple — jednolite API logowania, obsługujące wiele backendów (OSLog, konsola, pliki).
import Logging
var logger = Logger(label: "com.myapp.network")
logger.logLevel = .debug
logger.info("Zapytanie sieciowe rozpoczęte", metadata: [
"url": "\(requestURL)",
"method": "GET"
])
logger.warning("Czas odpowiedzi przekroczył 2 sekundy")
logger.error("Błąd połączenia: brak internetu")
Te trzy przykłady pokrywają typowe scenariusze użycia SPM: klienty HTTP, kontenery DI i infrastrukturę systemową. Wybór bibliotek nie jest przypadkowy — Alamofire, Swinject i swift-log znajdują się w top 20 najpopularniejszych pakietów Swift na GitHub.
Jeśli projekt używa CocoaPods lub Carthage, migracja na SPM odbywa się w kilku krokach. Proces jest bezpieczny: zależności SPM mogą współistnieć z CocoaPods i Carthage w jednym projekcie, co pozwala na stopniową migrację.
.xcworkspace, otwórz .xcodeproj i wykonaj Clean Build Folder.rm -rf Carthage/ w terminalu.Na 2025 rok SPM obsługuje zdecydowaną większość popularnych bibliotek Swift. Wyjątkiem są niektóre frameworki ObjC bez map modułów (modulemap). Jeśli biblioteka jeszcze nie obsługuje SPM — sprawdź sekcję Installation w jej README; większość autorów dodała już obsługę SPM w najnowszych wersjach.
Często zadawane pytania
SPM jest wbudowany w kompilator Swift i Xcode, nie wymaga instalacji przez gem lub Homebrew. CocoaPods używa scentralizowanego rejestru Specs i generuje osobny workspace. Carthage działa przez frameworki bez integracji z projektem. SPM to jedyny menedżer zintegrowany na poziomie kompilatora: zależności są rozwiązywane, buforowane i budowane równolegle z głównym kodem.
Tak, SPM obsługuje projekty mieszane Swift + Objective-C. Pliki ObjC wewnątrz pakietu SPM automatycznie trafiają do Umbrella Header pod warunkiem poprawnego modulemap. Jednak SPM nie obsługuje statycznych bibliotek ObjC, które nie mają mapy modułów. Zaleca się podłączanie bibliotek ObjC przez SPM tylko jeśli udostępniają modulemap lub są napisane w czystym C.
SPM używa semantycznego versionowania (SemVer). Jeśli pakiet A wymaga Alamofire 5.8+, a pakiet B — Alamofire 5.9+, SPM wybierze wersję 5.9.x spełniającą oba wymagania. Jeśli konflikt jest nierozwiązywalny (jeden pakiet wymaga 5.x, drugi — 6.x), SPM zgłosi błąd. W takim przypadku należy zaktualizować jeden z pakietów lub przełączyć zależność na wersję kompatybilną z oboma wymaganiami.
Na macOS: ~Library/Caches/org.swift.swiftpm/ i ~/Library/Developer/Xcode/DerivedData/. Na Linux: ~cache/swiftpm/. Podczas budowania SPM buforuje kod źródłowy i skompilowane pliki obiektowe. Aby całkowicie wyczyścić cache, wykonaj swift package reset — to polecenie usuwa cache zależności i DerivedData dla bieżącego projektu.
Tak, od Swift 5.2 SPM obsługuje cele binarne (binary targets). Zamknięta biblioteka jest dostarczana jako XCFramework, a w Package.swift podaje się ścieżkę do .xcframework. Kod źródłowy nie jest ujawniany. Binary target jest określany przez .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework"). Pozwala to podłączać komercyjne SDK bez naruszania umów licencyjnych.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również