SPM: cos'è, Swift Package Manager e Package.swift

Autore: IT Sectr Pubblicato: 2026-02-13 Tempo di lettura: 11 min

SPM (Swift Package Manager) è un gestore di pacchetti integrato nell'ecosistema Swift, sviluppato da Apple per automatizzare la connessione, compilazione e aggiornamento delle librerie di terze parti. SPM fa parte del compilatore Swift dalla versione 3.0 (2016) e non richiede installazione separata. A differenza di CocoaPods e Carthage, SPM si integra direttamente con il compilatore e Xcode, diventando lo strumento standard di gestione delle dipendenze nei progetti Swift moderni. In questo articolo analizzeremo la struttura di Package.swift, i comandi SPM, la creazione di pacchetti personalizzati e la migrazione da gestori alternativi.

Punti chiave

  • SPM (Swift Package Manager) è un gestore di pacchetti integrato nel compilatore Swift che non richiede installazione separata; funziona su iOS, macOS, Linux e piattaforme server.
  • Package.swift è un file manifesto che descrive il nome del pacchetto, le piattaforme, le dipendenze e i moduli target in formato dichiarativo.
  • SPM risolve le dipendenze tramite versionamento semantico (SemVer), memorizza nella cache il codice sorgente e compila i pacchetti in parallelo per velocizzare.
  • Comandi: swift package init (creare pacchetto), swift package update (aggiornare dipendenze), swift build (compilare), swift test (eseguire test).
  • La migrazione da CocoaPods/Carthage a SPM si effettua tramite Xcode: File → Add Package Dependency, dopo di che podfile e Cartfile vengono rimossi.

Cos'è SPM?

SPM (Swift Package Manager) è il gestore di pacchetti ufficiale per il linguaggio Swift, integrato nel compilatore swiftc e nell'ambiente di sviluppo Xcode. Consente agli sviluppatori di aggiungere librerie di terze parti, gestire le loro versioni e pubblicare i propri pacchetti. SPM è apparso per la prima volta in Swift 3.0 (settembre 2016) come strumento a riga di comando e, a partire da Xcode 11 (2019), ha ricevuto l'integrazione completa con l'interfaccia grafica — le dipendenze vengono ora aggiunte tramite il menu File → Add Packages.

SPM scarica automaticamente il codice sorgente delle dipendenze dai repository Git, le compila in parallelo con il progetto principale e memorizza nella cache i risultati per velocizzare le compilazioni successive. A differenza di CocoaPods, SPM non genera un workspace separato (xcworkspace) — le dipendenze diventano parte del progetto Xcode principale. Secondo il sondaggio Swift.org Developer Survey (2024), il 67% degli sviluppatori iOS utilizza SPM, rendendolo lo strumento di gestione delle dipendenze più popolare nell'ecosistema Swift.

SPM supporta tre piattaforme: Apple (iOS, macOS, tvOS, watchOS, visionOS), Linux (Ubuntu, CentOS, Amazon Linux) e Swift lato server (Vapor, Kitura). Su Linux, SPM funziona interamente tramite riga di comando senza Xcode.

Come funziona Swift Package Manager

SPM è costruito attorno a tre concetti chiave: pacchetti (packages), prodotti (products) e target (targets). Un pacchetto è un repository Git con un manifesto Package.swift. Un prodotto è il risultato della compilazione (una libreria o un eseguibile). Un target è un modulo all'interno del pacchetto che viene compilato in un'unità di build.

Quando uno sviluppatore aggiunge una dipendenza a Package.swift, SPM esegue i seguenti passaggi:

  1. Clonazione — SPM scarica il repository Git della dipendenza dall'URL specificato.
  2. Risoluzione delle versioni — analizza i tag SemVer (ad esempio 2.1.3) e seleziona la versione appropriata nell'intervallo specificato.
  3. Risoluzione transitiva — verifica le dipendenze delle dipendenze e costruisce un grafo di versioni senza conflitti.
  4. Cache — salva il codice sorgente scaricato in ~Library/Caches/org.swift.swiftpm/.
  5. Compilazione — compila tutti i target del pacchetto con i flag del progetto principale.

Il file Package.resolved fissa le versioni esatte di tutte le dipendenze in modo che il team di sviluppo lavori con un set identico di librerie. Questo file deve essere aggiunto al controllo versione (git).

Un vantaggio chiave di SPM rispetto alle alternative è l'assenza di un registro centralizzato. I pacchetti possono risiedere in qualsiasi repository Git pubblico: GitHub, GitLab, Bitbucket, oltre che su server Git privati aziendali. Da Swift 5.2, SPM supporta dipendenze binarie (binary targets) — librerie chiuse distribuite come XCFramework senza fornire codice sorgente.

Package.swift — manifesto del progetto

Package.swift è un file Swift che descrive la struttura del pacchetto e le sue dipendenze. Il file è scritto in Swift stesso (non JSON o YAML), consentendo l'uso di logica condizionale, costanti calcolate e funzioni all'interno del manifesto.

Struttura di base di Package.swift:

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"]
        ),
    ]
)

Analizziamo gli elementi chiave:

  • // swift-tools-version: 5.9 — direttiva che specifica la versione di SPM; da essa dipende la sintassi disponibile del manifesto.
  • name — nome del pacchetto, visualizzato in Xcode e utilizzato nei collegamenti delle dipendenze.
  • platforms — versioni minime delle piattaforme; SPM non consentirà di compilare il pacchetto su una versione di SO più vecchia.
  • products — ciò che il pacchetto "esporta": una libreria (.library) o un eseguibile (.executable).
  • dependencies — elenco di pacchetti esterni con URL e versione; supporta from:, exact:, branch:, revision:.
  • targets — target di compilazione; ogni target contiene un elenco di dipendenze, risorse e file Swift dalla directory corrispondente (Sources/TargetName/).

Esempio di specifica di versione esatta, branch e commit:

swift
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"),
]

Da Swift 5.9, Package.swift ha aggiunto il supporto per static framework e linkerSettings, consentendo una configurazione più precisa del linker per librerie statiche e dinamiche.

Comandi SPM di base

Swift Package Manager fornisce un insieme di comandi per lavorare tramite terminale. I comandi vengono eseguiti dalla directory principale del pacchetto (dove si trova Package.swift).

bash
# Creare un nuovo pacchetto con una libreria
swift package init --type library

# Creare un pacchetto eseguibile (applicazione console)
swift package init --type executable

# Compilare il progetto
swift build

# Compilare in configurazione release
swift build -c release

# Eseguire i test
swift test

# Eseguire un test specifico
swift test --filter "MyLibraryTests/testExample"

# Scaricare e risolvere le dipendenze
swift package resolve

# Aggiornare le dipendenze alle ultime versioni disponibili
swift package update

# Mostrare il grafo delle dipendenze
swift package show-dependencies

# Pulire la cache di compilazione
swift package clean

# Generare il progetto Xcode (prima di Xcode 11)
swift package generate-xcodeproj

Quando si lavora all'interno di Xcode, la maggior parte di questi comandi viene eseguita automaticamente: le dipendenze vengono risolte all'apertura del progetto, la compilazione si avvia con ⌘B, i test con ⌘U. Tuttavia, la conoscenza dei comandi da terminale è necessaria per le pipeline CI/CD (GitHub Actions, GitLab CI, Jenkins) dove Xcode non è disponibile.

Il comando swift package resolve crea o aggiorna il file Package.resolved. Questo file fissa le versioni esatte di tutte le dipendenze, incluse quelle transitive, e deve essere aggiunto a git. Si consiglia di eseguire swift package update prima di ogni nuovo branch di funzionalità per lavorare con versioni aggiornate delle librerie.

Creazione del tuo pacchetto

Creare il proprio pacchetto SPM è utile per incapsulare la logica di business in progetti multi-modulo e per pubblicare librerie open source. Vediamo il processo passo dopo passo.

Passo 1: Inizializzazione

bash
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library

Passo 2: Struttura delle directory

Il comando swift package init crea la seguente struttura:

text
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│   └── MyNetworkKit/
│       └── MyNetworkKit.swift
└── Tests/
    └── MyNetworkKitTests/
        └── MyNetworkKitTests.swift

SPM scansiona automaticamente le directory Sources/ e Tests/: ogni sottodirectory all'interno di Sources corrisponde a un target.

Passo 3: Modificare Package.swift

Aggiungiamo le dipendenze e configuriamo le piattaforme target:

swift
// 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"]
        ),
    ]
)

Passo 4: Scrivere il codice

swift
// 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
    }
}

Passo 5: Pubblicazione

>Spingi il pacchetto su un repository Git e crea un tag SemVer:
bash
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

Dopodiché, qualsiasi sviluppatore può aggiungere il tuo pacchetto usando .package(url: "https://github.com/username/MyNetworkKit.git", from: "1.0.0").

Esempi di utilizzo di SPM

Esempio 1: Aggiungere Alamofire per richieste di rete

Alamofire è il client HTTP più popolare per Swift. Aggiungiamolo tramite SPM ed eseguiamo una richiesta GET.

swift
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("Ricevuti (users.count) utenti")
            case .failure(let error):
                print("Errore: (error.localizedDescription)")
            }
        }
}

Esempio 2: Swinject — dependency injection

La libreria Swinject fornisce un contenitore DI per Swift. Si aggiunge tramite .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0").

swift
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()

Esempio 3: Swift-log per logging strutturato

Il pacchetto swift-log di Apple fornisce un'API di logging unificata che supporta più backend (OSLog, console, file).

swift
import Logging

var logger = Logger(label: "com.myapp.network")
logger.logLevel = .debug

logger.info("Richiesta di rete avviata", metadata: [
    "url": "(requestURL)",
    "method": "GET"
])

logger.warning("Il tempo di risposta ha superato i 2 secondi")
logger.error("Errore di connessione: nessuna connessione internet")

Questi tre esempi coprono scenari tipici di utilizzo di SPM: client HTTP, contenitori DI e infrastruttura di sistema. La selezione delle librerie non è casuale — Alamofire, Swinject e swift-log sono tra i 20 pacchetti Swift con più stelle su GitHub.

Migrazione da CocoaPods e Carthage

Se il tuo progetto utilizza CocoaPods o Carthage, la migrazione a SPM si effettua in pochi passaggi. Il processo è sicuro: le dipendenze SPM possono coesistere con CocoaPods e Carthage nello stesso progetto, consentendo una migrazione graduale.

CocoaPods → SPM

  1. In Xcode: File → Add Package Dependency, inserisci l'URL del pacchetto.
  2. Seleziona la versione e aggiungi il pacchetto ai target necessari.
  3. Dopo aver aggiunto tutte le dipendenze tramite SPM, rimuovi le righe dal Podfile.
  4. Rimuovi il .xcworkspace, apri il .xcodeproj ed esegui Clean Build Folder.

Carthage → SPM

  1. Aggiungi i pacchetti tramite Xcode File → Add Package Dependency.
  2. Rimuovi le dipendenze dal Cartfile.
  3. Rimuovi gli script di compilazione Carthage da Build Phases.
  4. Pulisci la cache: rm -rf Carthage/ nel terminale.

A partire dal 2025, SPM supporta la stragrande maggioranza delle librerie Swift popolari. Le eccezioni sono alcuni framework ObjC senza mappe di moduli. Se una libreria non supporta ancora SPM — controlla la sezione Installation nel suo README; la maggior parte degli autori ha già aggiunto il supporto SPM nelle ultime versioni.

Domande frequenti

In cosa SPM differisce da CocoaPods e Carthage?

SPM è integrato nel compilatore Swift e in Xcode, non richiede installazione tramite gem o Homebrew. CocoaPods utilizza un registro centralizzato Specs e genera un workspace separato. Carthage funziona tramite framework senza integrazione con il progetto. SPM è l'unico gestore integrato a livello di compilatore: le dipendenze vengono risolte, memorizzate nella cache e compilate in parallelo con il codice principale.

Si può usare SPM per progetti Objective-C?

Sì, SPM supporta progetti misti Swift + Objective-C. I file ObjC all'interno di un pacchetto SPM vengono automaticamente inclusi in un Umbrella Header a condizione che esista un modulemap corretto. Tuttavia, SPM non supporta librerie statiche ObjC senza mappa di moduli. Si consiglia di collegare librerie ObjC tramite SPM solo se forniscono un modulemap o sono scritte in C puro.

Come risolve SPM i conflitti di versione?

SPM utilizza il versionamento semantico (SemVer). Se il pacchetto A richiede Alamofire 5.8+ e il pacchetto B richiede Alamofire 5.9+, SPM selezionerà la versione 5.9.x che soddisfa entrambi. Se il conflitto è irrisolvibile (un pacchetto richiede 5.x, un altro 6.x), SPM segnalerà un errore. In tal caso, è necessario aggiornare uno dei pacchetti o modificare la dipendenza verso una versione compatibile con entrambi i requisiti.

Dove vengono memorizzati i pacchetti SPM scaricati?

Su macOS: ~Library/Caches/org.swift.swiftpm/ e ~/Library/Developer/Xcode/DerivedData/. Su Linux: ~cache/swiftpm/. Durante le compilazioni, SPM memorizza nella cache il codice sorgente e i file oggetto compilati. Per pulire completamente la cache, esegui swift package reset — questo comando rimuove la cache delle dipendenze e DerivedData per il progetto corrente.

SPM supporta librerie chiuse (proprietarie)?

Sì, da Swift 5.2 SPM supporta i target binari (binary targets). Una libreria chiusa viene distribuita come XCFramework e il percorso al .xcframework viene specificato in Package.swift. Il codice sorgente non viene esposto. Un target binario viene specificato tramite .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework"). Ciò consente di collegare SDK commerciali senza violare accordi di licenza.

Riepilogo

  • SPM (Swift Package Manager) è un gestore di pacchetti integrato in Swift che non richiede installazione separata ed è integrato con Xcode e il compilatore.
  • Package.swift è un manifesto dichiarativo scritto in Swift che descrive il nome del pacchetto, le piattaforme, le dipendenze, i prodotti e i target di compilazione.
  • SPM utilizza repository Git come fonti di pacchetti e risolve le versioni tramite SemVer, memorizzando nella cache il codice sorgente per compilazioni successive più veloci.
  • Comandi principali: swift package init (creare pacchetto), swift build (compilare), swift test (testare), swift package update (aggiornare dipendenze).
  • Un pacchetto personalizzato viene creato tramite swift package init, pubblicato su Git e reso disponibile ad altri progetti tramite URL con tag SemVer.
  • La migrazione da CocoaPods/Carthage a SPM è sicura: le dipendenze possono coesistere, la migrazione avviene tramite File → Add Package Dependency in Xcode.
  • SPM è lo strumento standard di gestione delle dipendenze nell'ecosistema Swift, utilizzato dal 67% degli sviluppatori iOS (Swift.org Developer Survey, 2024).

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche