CocoaPods estuvo durante años en el centro de la gestión de dependencias en proyectos iOS. Carthage fue una alternativa más ligera. Pero desde que Swift Package Manager (SPM) llegó con Swift 3 en 2016 y Apple lo integró de verdad en Xcode con la versión 11 (2019), el panorama ha cambiado. En 2026, SPM es la herramienta oficial y el estándar de facto para proyectos iOS, macOS, Linux y server-side Swift. Este artículo explica cómo usarlo en profundidad y qué ofrece que CocoaPods no tenía.
Estructura de un Package.swift
Package.swift es el manifiesto del paquete, escrito en Swift. Nada de Ruby, nada de YAML:
// swift-tools-version: 5.10
import PackageDescription
let package = Package(
name: "MiApp",
platforms: [
.iOS(.v17),
.macOS(.v14)
],
dependencies: [
.package(
url: "https://github.com/apple/swift-argument-parser.git",
from: "1.3.0"
),
.package(
url: "https://github.com/vapor/vapor.git",
from: "4.99.0"
)
],
targets: [
.target(
name: "MiApp",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "Vapor", package: "vapor")
]
),
.testTarget(
name: "MiAppTests",
dependencies: ["MiApp"]
)
]
)
La versión mínima de las herramientas (swift-tools-version) determina qué APIs del manifiesto están disponibles. Es recomendable usar 5.9 o 5.10 en proyectos nuevos para acceder a las últimas características.
Gestión de versiones con resolución semántica
SPM usa versionado semántico (semver) para resolver dependencias. Tienes tres formas de especificar una versión:
// Versión mínima (más común): acepta cualquier patch y minor superior .package(url: "https://github.com/...", from: "2.0.0") // Rango exacto .package(url: "https://github.com/...", "2.0.0" ..< "3.0.0") // Rama o commit exacto (útil durante desarrollo) .package(url: "https://github.com/...", branch: "main") .package(url: "https://github.com/...", revision: "abc1234")
El fichero Package.resolved registra las versiones exactas resueltas. Debe incluirse en el repositorio para reproducibilidad, igual que package-lock.json en npm.
Targets locales y modularización
SPM facilita dividir un proyecto en módulos sin crear repositorios separados. Los targets locales viven en el mismo Package.swift:
targets: [
.target(name: "Networking"),
.target(name: "Persistence"),
.target(
name: "MiApp",
dependencies: ["Networking", "Persistence"]
),
.testTarget(
name: "NetworkingTests",
dependencies: ["Networking"]
)
]
Cada target tiene su propio directorio bajo Sources/. Esta estructura fuerza buenas prácticas: visibilidad explícita con internal y public, dependencias entre módulos declaradas explícitamente.
Plugins: la característica que cambia las reglas
Desde Swift 5.6, SPM incluye un sistema de plugins que permiten ejecutar herramientas durante el proceso de compilación o como comandos adicionales. Esto elimina muchos casos de uso de scripts externos o de herramientas como Makefile.
Build plugins: generación de código en compilación
.plugin(
name: "GenerateResources",
capability: .buildTool(),
dependencies: [
.product(name: "ResourceGen", package: "mi-generador")
]
)
Un build plugin recibe la lista de archivos fuente y puede generar código Swift adicional que se compila junto con el proyecto. Útil para generar constantes de recursos, código a partir de archivos protobuf o localización tipada.
Command plugins: linting y formateo
// Ejecutar un plugin de comando: swift package plugin format-source-code
SwiftFormat y SwiftLint tienen plugins SPM oficiales. Se integran sin scripts externos ni fases de compilación manuales en Xcode.
Recursos incluidos en paquetes
Desde SPM 5.3, los targets pueden incluir recursos: imágenes, archivos de configuración, ficheros de localización:
.target(
name: "MiLibrería",
resources: [
.process("Assets.xcassets"),
.copy("Configuracion/default.json")
]
)
SPM genera automáticamente un bundle accesible desde el código con Bundle.module:
let imagen = UIImage(named: "logo", in: .module, with: nil) let url = Bundle.module.url(forResource: "default", withExtension: "json")
Integración en Xcode vs línea de comandos
En Xcode, basta con ir a File > Add Package Dependencies y pegar la URL del repositorio. Xcode resuelve, descarga y muestra las versiones disponibles. La integración es tan fluida que ha hecho que CocoaPods deje de ser necesario en la mayoría de proyectos.
En línea de comandos, los comandos básicos son:
# Resolver y descargar dependencias swift package resolve # Compilar swift build # Ejecutar tests swift test # Actualizar dependencias swift package update # Generar proyecto Xcode (útil pero no siempre necesario) swift package generate-xcodeproj
Cuándo todavía puede hacer falta CocoaPods
La mayoría de librerías populares (Alamofire, Kingfisher, Firebase desde 2023, Realm desde 8.x) tienen soporte SPM. Los casos donde aún puede hacer falta CocoaPods son librerías muy antiguas sin mantenimiento activo o proyectos con dependencias que requieren fases de compilación de CocoaPods muy específicas (por ejemplo, algunos SDKs de análisis propietarios).
Para proyectos nuevos en 2026, SPM es el punto de partida natural. Si usas TypeScript en el frontend de tu stack, el artículo sobre TypeScript 5.8 y su gestión de módulos ofrece una perspectiva paralela sobre cómo los ecosistemas modernos gestionan dependencias de forma tipada y declarativa.
Imagen: Pexels / Daniil Komov
