SPM (Swift Package Manager) é um gerenciador de pacotes integrado ao ecossistema Swift, desenvolvido pela Apple para automatizar a conexão, compilação e atualização de bibliotecas de terceiros. O SPM faz parte do compilador Swift desde a versão 3.0 (2016) e não requer instalação separada. Ao contrário do CocoaPods e Carthage, o SPM se integra diretamente ao compilador e ao Xcode, tornando-se a ferramenta padrão de gerenciamento de dependências em projetos Swift modernos. Neste artigo, analisaremos a estrutura do Package.swift, comandos do SPM, criação de pacotes próprios e migração de gerenciadores alternativos.
Pontos principais
SPM (Swift Package Manager) é o gerenciador oficial de pacotes para a linguagem Swift, integrado ao compilador swiftc e ao ambiente de desenvolvimento Xcode. Ele permite que desenvolvedores adicionem bibliotecas de terceiros, gerenciem suas versões e publiquem seus próprios pacotes. O SPM apareceu pela primeira vez no Swift 3.0 (setembro de 2016) como ferramenta de linha de comando e, a partir do Xcode 11 (2019), recebeu integração completa com a interface gráfica — as dependências agora são adicionadas através do menu File → Add Packages.
O SPM baixa automaticamente o código fonte das dependências de repositórios Git, compila-os em paralelo com o projeto principal e armazena em cache os resultados para que compilações subsequentes sejam mais rápidas. Ao contrário do CocoaPods, o SPM não gera um workspace separado (xcworkspace) — as dependências se tornam parte do projeto principal do Xcode. De acordo com a pesquisa Swift.org Developer Survey (2024), 67% dos desenvolvedores iOS usam o SPM, tornando-o a ferramenta de gerenciamento de dependências mais popular do ecossistema Swift.
O SPM suporta três plataformas: Apple (iOS, macOS, tvOS, watchOS, visionOS), Linux (Ubuntu, CentOS, Amazon Linux) e Swift do lado do servidor (Vapor, Kitura). No Linux, o SPM funciona inteiramente via linha de comando sem Xcode.
O SPM é construído em torno de três conceitos-chave: pacotes (packages), produtos (products) e alvos (targets). Um pacote é um repositório Git com um manifesto Package.swift. Um produto é o resultado da compilação (uma biblioteca ou um executável). Um alvo é um módulo dentro do pacote que compila em uma unidade de compilação.
Quando um desenvolvedor adiciona uma dependência ao Package.swift, o SPM executa as seguintes etapas:
~Library/Caches/org.swift.swiftpm/.O arquivo Package.resolved fixa as versões exatas de todas as dependências para que a equipe de desenvolvimento trabalhe com um conjunto idêntico de bibliotecas. Este arquivo deve ser adicionado ao controle de versão (git).
Uma vantagem chave do SPM sobre alternativas é a ausência de um registro centralizado. Os pacotes podem residir em qualquer repositório Git público: GitHub, GitLab, Bitbucket, bem como em servidores Git privados da empresa. Desde o Swift 5.2, o SPM suporta dependências binárias (binary targets) — bibliotecas fechadas distribuídas como XCFramework sem fornecer código fonte.
Package.swift é um arquivo Swift que descreve a estrutura do pacote e suas dependências. O arquivo é escrito no próprio Swift (não JSON ou YAML), o que permite usar lógica condicional, constantes calculadas e funções dentro do manifesto.
Estrutura básica do 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"]
),
]
)
Vamos analisar os elementos-chave:
// swift-tools-version: 5.9 — diretiva que especifica a versão do SPM; a sintaxe disponível do manifesto depende dela.name — nome do pacote, exibido no Xcode e usado nos links de dependências.platforms — versões mínimas das plataformas; o SPM não permitirá compilar o pacote em uma versão de SO mais antiga.products — o que o pacote "exporta": uma biblioteca (.library) ou um executável (.executable).dependencies — lista de pacotes externos com URL e versão; suporta from:, exact:, branch:, revision:.targets — alvos de compilação; cada alvo contém uma lista de dependências, recursos e arquivos swift do diretório correspondente (Sources/TargetName/).Exemplo de especificação de versão exata, branch e 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"),
]
Desde o Swift 5.9, o Package.swift adicionou suporte para static framework e linkerSettings, permitindo uma configuração mais precisa do linker para bibliotecas estáticas e dinâmicas.
O Swift Package Manager fornece um conjunto de comandos para trabalhar através do terminal. Os comandos são executados a partir do diretório raiz do pacote (onde o Package.swift está localizado).
# Criar um novo pacote com uma biblioteca
swift package init --type library
# Criar um pacote executável (aplicativo de console)
swift package init --type executable
# Compilar o projeto
swift build
# Compilar em configuração de release
swift build -c release
# Executar testes
swift test
# Executar um teste específico
swift test --filter "MyLibraryTests/testExample"
# Baixar e resolver dependências
swift package resolve
# Atualizar dependências para as últimas versões disponíveis
swift package update
# Mostrar grafo de dependências
swift package show-dependencies
# Limpar cache de compilação
swift package clean
# Gerar projeto Xcode (antes do Xcode 11)
swift package generate-xcodeproj
Ao trabalhar dentro do Xcode, a maioria desses comandos é executada automaticamente: as dependências são resolvidas ao abrir o projeto, a compilação inicia com ⌘B, os testes com ⌘U. No entanto, conhecer os comandos de terminal é necessário para pipelines de CI/CD (GitHub Actions, GitLab CI, Jenkins) onde o Xcode não está disponível.
O comando swift package resolve cria ou atualiza o arquivo Package.resolved. Este arquivo fixa as versões exatas de todas as dependências, incluindo as transitivas, e deve ser adicionado ao git. Recomenda-se executar swift package update antes de cada nova branch de funcionalidade para trabalhar com versões atualizadas das bibliotecas.
Criar seu próprio pacote SPM é útil para encapsular lógica de negócios em projetos multimódulo e para publicar bibliotecas de código aberto. Vamos ver o processo passo a passo.
mkdir MyNetworkKit
cd MyNetworkKit
swift package init --type library
O comando swift package init cria a seguinte estrutura:
MyNetworkKit/
├── Package.swift
├── README.md
├── Sources/
│ └── MyNetworkKit/
│ └── MyNetworkKit.swift
└── Tests/
└── MyNetworkKitTests/
└── MyNetworkKitTests.swift
O SPM examina automaticamente os diretórios Sources/ e Tests/: cada subdiretório dentro de Sources corresponde a um alvo (target).
Vamos adicionar dependências e configurar as plataformas alvo:
// 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
}
}
Envie o pacote para um repositório Git e crie uma 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
Depois disso, qualquer desenvolvedor pode adicionar seu pacote usando .package(url: "https://github.com/username/MyNetworkKit.git", from: "1.0.0").
Alamofire é o cliente HTTP mais popular para Swift. Vamos adicioná-lo via SPM e fazer uma requisição 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("Recebidos (users.count) usuários")
case .failure(let error):
print("Erro: (error.localizedDescription)")
}
}
}
A biblioteca Swinject fornece um contêiner DI para Swift. É adicionada 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()
O pacote swift-log da Apple fornece uma API de registro unificada que suporta múltiplos backends (OSLog, console, arquivos).
import Logging
var logger = Logger(label: "com.myapp.network")
logger.logLevel = .debug
logger.info("Requisição de rede iniciada", metadata: [
"url": "(requestURL)",
"method": "GET"
])
logger.warning("Tempo de resposta excedeu 2 segundos")
logger.error("Erro de conexão: sem internet")
Estes três exemplos cobrem cenários típicos de uso do SPM: clientes HTTP, contêineres DI e infraestrutura de sistema. A seleção de bibliotecas não é acidental — Alamofire, Swinject e swift-log estão entre os 20 pacotes Swift com mais estrelas no GitHub.
Se seu projeto usa CocoaPods ou Carthage, a migração para SPM é feita em poucos passos. O processo é seguro: as dependências do SPM podem coexistir com CocoaPods e Carthage no mesmo projeto, permitindo migração gradual.
.xcworkspace, abra o .xcodeproj e execute Clean Build Folder.rm -rf Carthage/ no terminal.A partir de 2025, o SPM suporta a grande maioria das bibliotecas Swift populares. Exceções são alguns frameworks ObjC sem mapas de módulo. Se uma biblioteca ainda não suporta SPM — verifique a seção Installation no README dela; a maioria dos autores já adicionou suporte SPM nas versões mais recentes.
Perguntas frequentes
O SPM está integrado ao compilador Swift e ao Xcode, não requer instalação via gem ou Homebrew. O CocoaPods usa um registro centralizado Specs e gera um workspace separado. O Carthage funciona através de frameworks sem integração com o projeto. O SPM é o único gerenciador integrado no nível do compilador: as dependências são resolvidas, armazenadas em cache e compiladas em paralelo com o código principal.
Sim, o SPM suporta projetos mistos Swift + Objective-C. Arquivos ObjC dentro de um pacote SPM são automaticamente incluídos em um Umbrella Header desde que exista um modulemap correto. No entanto, o SPM não suporta bibliotecas estáticas ObjC que não possuam um mapa de módulo. Recomenda-se conectar bibliotecas ObjC via SPM apenas se elas fornecerem um modulemap ou forem escritas em C puro.
O SPM usa versionamento semântico (SemVer). Se o pacote A requer Alamofire 5.8+ e o pacote B requer Alamofire 5.9+, o SPM selecionará a versão 5.9.x que satisfaz ambos. Se o conflito for irreconciliável (um pacote requer 5.x, outro requer 6.x), o SPM reportará um erro. Nesse caso, você precisa atualizar um dos pacotes ou alterar a dependência para uma versão compatível com ambos os requisitos.
No macOS: ~Library/Caches/org.swift.swiftpm/ e ~/Library/Developer/Xcode/DerivedData/. No Linux: ~cache/swiftpm/. Durante as compilações, o SPM armazena em cache o código fonte e os arquivos objeto compilados. Para limpar totalmente o cache, execute swift package reset — este comando remove o cache de dependências e o DerivedData do projeto atual.
Sim, desde o Swift 5.2 o SPM suporta alvos binários (binary targets). Uma biblioteca fechada é distribuída como XCFramework, e o caminho para o .xcframework é especificado no Package.swift. O código fonte não é exposto. Um alvo binário é especificado via .binaryTarget(name: "PrivateSDK", path: "Sources/PrivateSDK.xcframework"). Isso permite conectar SDKs comerciais sem violar acordos de licença.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também