SPM (Swift Package Manager) est un gestionnaire de paquets intégré à l'écosystème Swift, développé par Apple pour automatiser la connexion, la compilation et la mise à jour des bibliothèques tierces. SPM fait partie du compilateur Swift depuis la version 3.0 (2016) et ne nécessite aucune installation séparée. Contrairement à CocoaPods et Carthage, SPM s'intègre directement au compilateur et à Xcode, ce qui en fait l'outil standard de gestion des dépendances dans les projets Swift modernes. Dans cet article, nous analyserons la structure de Package.swift, les commandes SPM, la création de paquets personnalisés et la migration depuis les gestionnaires alternatifs.
Points clés
SPM (Swift Package Manager) est le gestionnaire de paquets officiel pour le langage Swift, intégré au compilateur swiftc et à l'environnement de développement Xcode. Il permet aux développeurs d'ajouter des bibliothèques tierces, de gérer leurs versions et de publier leurs propres paquets. SPM est apparu pour la première fois dans Swift 3.0 (septembre 2016) en tant qu'outil en ligne de commande, et à partir de Xcode 11 (2019), il a reçu une intégration complète avec l'interface graphique — les dépendances sont désormais ajoutées via le menu File → Add Packages.
SPM télécharge automatiquement le code source des dépendances depuis les dépôts Git, les compile en parallèle du projet principal et met en cache les résultats pour que les compilations ultérieures soient plus rapides. Contrairement à CocoaPods, SPM ne génère pas d'espace de travail séparé (xcworkspace) — les dépendances font partie du projet Xcode principal. Selon l'enquête Swift.org Developer Survey (2024), 67 % des développeurs iOS utilisent SPM, ce qui en fait l'outil de gestion des dépendances le plus populaire de l'écosystème Swift.
SPM prend en charge trois plates-formes : Apple (iOS, macOS, tvOS, watchOS, visionOS), Linux (Ubuntu, CentOS, Amazon Linux) et Swift côté serveur (Vapor, Kitura). Sous Linux, SPM fonctionne entièrement en ligne de commande sans Xcode.
SPM est construit autour de trois concepts clés : les paquets (packages), les produits (products) et les cibles (targets). Un paquet est un dépôt Git avec un manifeste Package.swift. Un produit est le résultat de la compilation (une bibliothèque ou un exécutable). Une cible est un module à l'intérieur du paquet qui se compile en une unité de construction.
Lorsqu'un développeur ajoute une dépendance à Package.swift, SPM effectue les étapes suivantes :
~Library/Caches/org.swift.swiftpm/.Le fichier Package.resolved fige les versions exactes de toutes les dépendances afin que l'équipe de développement travaille avec un ensemble identique de bibliothèques. Ce fichier doit être ajouté au contrôle de version (git).
Un avantage clé de SPM par rapport aux alternatives est l'absence de registre centralisé. Les paquets peuvent résider dans n'importe quel dépôt Git public : GitHub, GitLab, Bitbucket, ainsi que sur les serveurs Git privés de l'entreprise. Depuis Swift 5.2, SPM prend en charge les dépendances binaires (binary targets) — des bibliothèques fermées distribuées sous forme de XCFramework sans fournir de code source.
Package.swift est un fichier Swift qui décrit la structure du paquet et ses dépendances. Le fichier est écrit en Swift lui-même (pas en JSON ni YAML), ce qui permet d'utiliser la logique conditionnelle, les constantes calculées et les fonctions à l'intérieur du manifeste.
Structure de base de 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"]
),
]
)
Analysons les éléments clés :
// swift-tools-version: 5.9 — directive spécifiant la version de SPM ; la syntaxe disponible du manifeste en dépend.name — nom du paquet, affiché dans Xcode et utilisé dans les liens de dépendances.platforms — versions minimales des plates-formes ; SPM ne permettra pas de compiler le paquet sur une version d'OS plus ancienne.products — ce que le paquet « exporte » : une bibliothèque (.library) ou un exécutable (.executable).dependencies — liste des paquets externes avec URL et version ; prend en charge from:, exact:, branch:, revision:.targets — cibles de compilation ; chaque cible contient une liste de dépendances, de ressources et de fichiers Swift du répertoire correspondant (Sources/TargetName/).Exemple de spécification d'une version exacte, d'une branche et d'un commit :
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"),
]
Depuis Swift 5.9, Package.swift a ajouté la prise en charge de static framework et de linkerSettings, permettant une configuration plus précise de l'éditeur de liens pour les bibliothèques statiques et dynamiques.
Swift Package Manager fournit un ensemble de commandes pour travailler via le terminal. Les commandes sont exécutées depuis le répertoire racine du paquet (où se trouve Package.swift).
# Créer un nouveau paquet avec une bibliothèque
swift package init --type library
# Créer un paquet exécutable (application console)
swift package init --type executable
# Compiler le projet
swift build
# Compiler en configuration de release
swift build -c release
# Exécuter les tests
swift test
# Exécuter un test spécifique
swift test --filter "MyLibraryTests/testExample"
# Télécharger et résoudre les dépendances
swift package resolve
# Mettre à jour les dépendances vers les dernières versions disponibles
swift package update
# Afficher le graphe des dépendances
swift package show-dependencies
# Vider le cache de compilation
swift package clean
# Générer le projet Xcode (avant Xcode 11)
swift package generate-xcodeproj
Lorsqu'on travaille dans Xcode, la plupart de ces commandes s'exécutent automatiquement : les dépendances sont résolues à l'ouverture du projet, la compilation démarre avec ⌘B, les tests avec ⌘U. Cependant, la connaissance des commandes terminal est nécessaire pour les pipelines CI/CD (GitHub Actions, GitLab CI, Jenkins) où Xcode n'est pas disponible.
La commande swift package resolve crée ou met à jour le fichier Package.resolved. Ce fichier fige les versions exactes de toutes les dépendances, y compris transitives, et doit être ajouté à git. Il est recommandé d'exécuter swift package update avant chaque nouvelle branche de fonctionnalité pour travailler avec des versions à jour des bibliothèques.
Créer votre propre paquet SPM est utile pour encapsuler la logique métier dans des projets multi-modules et pour publier des bibliothèques open source. Examinons le processus étape par étape.
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library
La commande swift package init crée la structure suivante :
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│ └── MyNetworkKit/
│ └── MyNetworkKit.swift
└── Tests/
└── MyNetworkKitTests/
└── MyNetworkKitTests.swift
SPM analyse automatiquement les répertoires Sources/ et Tests/ : chaque sous-répertoire dans Sources correspond à une cible (target).
Ajoutons les dépendances et configurons les plates-formes cibles :
// 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
}
}
Poussez le paquet vers un dépôt Git et créez un 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
Après cela, tout développeur peut ajouter votre paquet en utilisant .package(url: "https://github.com/username/MyNetworkKit.git", from: "1.0.0").
Alamofire est le client HTTP le plus populaire pour Swift. Ajoutons-le via SPM et effectuons une requête 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) utilisateurs reçus")
case .failure(let error):
print("Erreur : (error.localizedDescription)")
}
}
}
La bibliothèque Swinject fournit un conteneur DI pour Swift. Elle s'ajoute via .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()
Le paquet swift-log d'Apple fournit une API de journalisation unifiée prenant en charge plusieurs backends (OSLog, console, fichiers).
import Logging
var logger = Logger(label: "com.myapp.network")
logger.logLevel = .debug
logger.info("Requête réseau démarrée", metadata: [
"url": "(requestURL)",
"method": "GET"
])
logger.warning("Le temps de réponse a dépassé 2 secondes")
logger.error("Erreur de connexion : pas d'internet")
Ces trois exemples couvrent les scénarios typiques d'utilisation de SPM : clients HTTP, conteneurs DI et infrastructure système. La sélection des bibliothèques n'est pas fortuite — Alamofire, Swinject et swift-log font partie des 20 paquets Swift les plus étoilés sur GitHub.
Si votre projet utilise CocoaPods ou Carthage, la migration vers SPM se fait en quelques étapes. Le processus est sûr : les dépendances SPM peuvent coexister avec CocoaPods et Carthage dans le même projet, ce qui permet une migration progressive.
.xcworkspace, ouvrez le .xcodeproj et effectuez Clean Build Folder.rm -rf Carthage/ dans le terminal.En 2025, SPM prend en charge la grande majorité des bibliothèques Swift populaires. Les exceptions sont quelques frameworks ObjC sans cartes de modules. Si une bibliothèque ne prend pas encore en charge SPM — consultez la section Installation de son README ; la plupart des auteurs ont déjà ajouté la prise en charge de SPM dans les dernières versions.
Questions fréquentes
SPM est intégré au compilateur Swift et à Xcode, il ne nécessite pas d'installation via gem ou Homebrew. CocoaPods utilise un registre centralisé Specs et génère un espace de travail séparé. Carthage fonctionne via des frameworks sans intégration au projet. SPM est le seul gestionnaire intégré au niveau du compilateur : les dépendances sont résolues, mises en cache et compilées en parallèle du code principal.
Oui, SPM prend en charge les projets mixtes Swift + Objective-C. Les fichiers ObjC dans un paquet SPM sont automatiquement inclus dans un Umbrella Header à condition qu'un modulemap correct existe. Cependant, SPM ne prend pas en charge les bibliothèques statiques ObjC sans carte de module. Il est recommandé de connecter les bibliothèques ObjC via SPM uniquement si elles fournissent un modulemap ou sont écrites en C pur.
SPM utilise le versionnement sémantique (SemVer). Si le paquet A nécessite Alamofire 5.8+ et le paquet B nécessite Alamofire 5.9+, SPM sélectionnera la version 5.9.x satisfaisant les deux. Si le conflit est irrésoluble (un paquet nécessite 5.x, un autre 6.x), SPM signalera une erreur. Dans ce cas, vous devez mettre à jour l'un des paquets ou modifier la dépendance vers une version compatible avec les deux exigences.
Sous macOS : ~Library/Caches/org.swift.swiftpm/ et ~/Library/Developer/Xcode/DerivedData/. Sous Linux : ~cache/swiftpm/. Lors des compilations, SPM met en cache le code source et les fichiers objet compilés. Pour vider complètement le cache, exécutez swift package reset — cette commande supprime le cache des dépendances et DerivedData pour le projet actuel.
Oui, depuis Swift 5.2, SPM prend en charge les cibles binaires (binary targets). Une bibliothèque fermée est distribuée sous forme de XCFramework, et le chemin vers le .xcframework est spécifié dans Package.swift. Le code source n'est pas divulgué. Une cible binaire est spécifiée via .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework"). Cela permet de connecter des SDK commerciaux sans violer les accords de licence.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi