Tu primer paquete de Swift
Al terminar: Crear el Swift Package Torneo, escribir let y var, y correr tu primer test con Swift Testing.
Al terminar tendrás el segundo esqueleto de Marcador: una carpeta de trabajo, Marcador-Swift, y dentro un paquete de Swift llamado Torneo, gemelo del módulo :torneo que escribiste en Kotlin. Todavía vacío: la plantilla compila y su único test pasa. Además habrás escrito tus primeras líneas de Swift (let y var) en un Playground de Xcode.
Importa porque toda la parte de Swift del curso (lenguaje, memoria y concurrencia) vive en este paquete, y se compila y se prueba desde la Terminal, igual que ./gradlew :torneo:test en Kotlin. Aquí aprendes dos cosas que no se repiten: moverte por la Terminal y leer Package.swift (el equivalente del build.gradle.kts y del catálogo). La regla de los puntos y tu primer test llegan en La regla de los puntos y tu primer test.
Temas: la Terminal básica, el Swift que usas, el paquete y Package.swift, y let/var con tipos e inferencia.
La Terminal en cinco órdenes
Hasta ahora usaste la Terminal para pegar órdenes que te dábamos. Para Swift vas a moverte con ella, así que aprende lo mínimo. La Terminal es la ventana donde escribes una orden de texto y pulsas Intro. Ábrela con Spotlight (Cmd+Espacio, escribe «Terminal» e Intro). Cada orden se ejecuta en una carpeta, la «carpeta actual», que la Terminal muestra al principio de la línea (al abrirla es tu carpeta personal, ~).
pwd(print working directory, «escribe la carpeta de trabajo»): dice dónde estás. Una carpeta personal se ve como/Users/tu-nombre; la~es un atajo que significa esa carpeta.ls(list, «lista»): enseña lo que hay en la carpeta actual.ls -aenseña también los ficheros ocultos, los que empiezan por punto.mkdir Nombre(make directory, «crea una carpeta»): crea la carpetaNombredentro de la actual.cd Nombre(change directory, «cambia de carpeta»): entra enNombre.cd ..sube a la carpeta que contiene a la actual.cda secas te lleva a~, ycd ~/Marcador-Swift/Torneova directo a esa ruta desde cualquier sitio.open .abre la carpeta actual en el Finder, para que veas con el ratón lo que has hecho en la Terminal. El punto.significa «esta carpeta».
Si una orden dice No such file or directory, casi siempre estás en otra carpeta de la que crees: escribe pwd y ls y mira. Cada vez que abras una Terminal nueva empiezas en ~, y la primera orden de cada lección de Swift será cd ~/Marcador-Swift/Torneo.
Qué Swift estás usando
Un toolchain es el conjunto de herramientas que compila Swift: el compilador (que traduce tu código a un programa) y el gestor de paquetes. El de Xcode es el que usarás en todo el curso. Tres órdenes, desde cualquier carpeta, para ver cuál usa tu Terminal:
which -a swift
xcrun --find swift
xcrun swift --versionwhich -a swiftlista losswiftde tuPATH(la lista de carpetas donde la Terminal busca programas), en orden. El primero es el que se ejecuta cuando escribesswift.xcrun --find swiftdice dónde está elswiftdel Xcode activo: una ruta dentro deXcode.app.xcrun swift --versionejecuta eseswifty dice su versión.
Con solo Xcode instalado, which -a swift da /usr/bin/swift y xcrun --find swift da otra ruta, dentro de Xcode.app. No son dos Swift: /usr/bin/swift es un atajo que llama al Swift de Xcode. Con Xcode 26 verás algo como esto (los números de compilación varían):
swift-driver version: 1.127.14.1 Apple Swift version 6.2.3 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)
Target: arm64-apple-macosx26.0xcrun es la orden de Apple que ejecuta una herramienta del Xcode activo. En toda la parte de Swift escribirás xcrun swift … y no swift …: así siempre usas el de Xcode, sea cual sea el orden de tu PATH (si algún día instalas otro Swift por tu cuenta, which -a swift lo enseñará delante). El paquete que vas a crear pide la versión 6.2 de las herramientas. Si tu xcrun swift --version dice 6.2 o superior, sigue; si dice menos, vuelve a Instalar Xcode y el simulador.
Un Swift más antiguo que el que pide el paquete se detiene con un error como este (aquí lo provocamos pidiendo la versión 7.0; en tu caso, los números son los de tu versión):
error: 'torneo': package 'torneo' is using Swift tools version 7.0.0 but the installed version is 6.2.3El error habla de la versión de las herramientas del paquete, no de tu código. La primera línea de Package.swift fija esa versión, y quien compila tiene que ser igual o más nuevo.
El paquete: la unidad de Swift
Un paquete (package) es una carpeta con un fichero Package.swift que describe qué código contiene y cómo se compila. El manifiesto (manifest) es ese Package.swift, y está escrito en Swift.
Si quieres leer más sobre el gestor de paquetes de Swift, está en la documentación de swift.org.
Si vienes de Kotlin: Package.swift hace a la vez de build.gradle.kts (qué se compila y qué depende de qué) y de libs.versions.toml (de dónde salen las librerías ajenas). El Swift Package Manager (SwiftPM) es la herramienta que lo lee, la que se ejecuta con xcrun swift build; hace el papel de Gradle y viene dentro del Swift de Xcode: no hay nada que instalar ni un wrapper.
Package.swift, línea a línea
Package.swift es la hoja de instrucciones de la caja: lo que init ha generado, con la explicación línea a línea en los comentarios (traducidos y en el orden del fichero). Todavía no lo tienes: lo crea swift package init en el paso de más abajo, y entonces puedes compararlo con este.
// swift-tools-version: 6.2
// OJO: no es un comentario cualquiera. Fija la versión mínima de herramientas
// que necesita el paquete y debe ir en la primera línea. Con 6.2 el modo de
// lenguaje por defecto es Swift 6. Si tu Xcode es más nuevo, init escribe la suya.
import PackageDescription // carga las piezas para describir un paquete (Package, .target…)
// El manifiesto es un valor de Swift: una constante llamada package.
// Swift ejecuta este fichero para saber qué hay que compilar.
let package = Package(
name: "Torneo", // nombre del paquete
products: [
// Un producto es lo que el paquete ofrece hacia fuera: una librería
// llamada Torneo, hecha con el objetivo Torneo. Es lo que la app de
// iPhone usará más adelante.
.library(
name: "Torneo",
targets: ["Torneo"]
),
],
targets: [
// Un objetivo es una pieza que se compila. Este compila los ficheros de
// Sources/Torneo: la lógica del torneo.
.target(
name: "Torneo"
),
// Un objetivo de tests: compila Tests/TorneoTests y depende de Torneo,
// para poder llamar a su código.
.testTarget(
name: "TorneoTests",
dependencies: ["Torneo"]
),
]
)Cómo se lee la sintaxis, que es la de cualquier llamada a una función de Swift:
[ … ]es una lista..library(…),.target(…)y.testTarget(…)son llamadas que crean cada pieza. El punto de delante significa «el tipo ya se sabe por el sitio donde estoy»: enproductsvan productos, así que Swift no necesita que escribas el tipo.name:ytargets:son argumentos con etiqueta: el nombre del argumento se escribe delante del valor. Funciones y switch lo cuenta a fondo.
Tres palabras que se confunden:
- Objetivo (target): un grupo de ficheros que se compila junto. Cada objetivo es un módulo con su nombre y su carpeta:
Sources/<objetivo>/oTests/<objetivo>/. - Producto (product): lo que el paquete ofrece hacia fuera, una librería o un ejecutable.
- Dependencia: algo que un objetivo necesita, otro objetivo del mismo paquete (
dependencies: ["Torneo"], como la del test) o un paquete de otra persona.
Paquetes de otras personas (solo para leer). Este paquete no usa código ajeno; cuando lo necesite, se pedirá en dos sitios de Package.swift, igual que en Gradle: se declara el paquete (de dónde sale y desde qué versión) y se nombra su producto en el objetivo que lo usa. No lo escribas:
let package = Package(
name: "Torneo",
products: [
.library(name: "Torneo", targets: ["Torneo"]),
],
dependencies: [
// De dónde sale el paquete y qué versión mínima (como una línea de [libraries]).
.package(url: "https://ejemplo.com/paquetes/swift-colecciones", from: "1.1.0"),
],
targets: [
.target(
name: "Torneo",
// Qué producto de ese paquete usa este objetivo (como `implementation(libs…)`).
dependencies: [.product(name: "Colecciones", package: "swift-colecciones")]
),
.testTarget(name: "TorneoTests", dependencies: ["Torneo"]),
]
)Al compilar, SwiftPM descarga el paquete y apunta la versión exacta que eligió en un fichero Package.resolved, junto a Package.swift; no se edita a mano. La primera dependencia llegará con la app de iPhone. Allí se añade desde un menú de Xcode (File → Add Package Dependencies…), que hace lo mismo sin que escribas nada: el proyecto de la app no tiene Package.swift.
let, var y la inferencia
La inferencia de tipos es que el compilador deduce el tipo mirando el valor: let total = 3 es un Int sin que lo escribas. Si quieres escribirlo, va detrás de dos puntos: let total: Int = 3.
Los tipos básicos que usarás son Int (enteros), Double (decimales), Bool (true/false) y String (texto). Swift no convierte tipos solo: multiplicar un Double por un Int da error, y hay que convertir a mano con el nombre del tipo como si fuera una función: Double(total) crea un Double a partir del Int total.
Un Playground para practicar
Para probar un ejemplo como el de abajo no hace falta un paquete: lo escribes en un Playground de Xcode, un fichero donde escribes Swift y ves el resultado al momento (Instalar Xcode y el simulador te lo presentó; aquí aprendes a crearlo). En toda la parte de Swift los ejemplos y los ejercicios se hacen ahí, y esta es la lección a la que las demás te remiten.
- En Xcode, menú File → New → Playground…
- Arriba, la pestaña macOS; elige la plantilla Blank y pulsa Next.
- Ponle un nombre, por ejemplo
Practica, guárdalo en Documentos y pulsa Create.
Se abre con unas líneas de plantilla, import Cocoa y var greeting. Puedes dejarlas o borrarlas; no molestan. Para cada ejemplo, escribe o pega el código debajo, y Xcode lo ejecuta solo: lo que imprime print sale en la consola, el panel de texto de abajo (View → Debug Area → Activate Console si no la ves), y a la derecha de cada línea aparece el valor que produce. Si no se ejecuta solo, pulsa el triángulo de abajo a la izquierda. Desde aquí, cuando una lección diga «tu Playground», es este: reutiliza siempre el mismo Playground y borra lo anterior antes de pegar un ejemplo nuevo. (Si ya tienes el MyPlayground de la instalación, puedes usarlo igual.)
let nombreDelTorneo = "Copa Barrio" // String, inferido
let totalDeJornadas: Int = 3 // el mismo tipo que se habría inferido
var jornada = 1 // var: se puede reasignar
jornada += 1
print("Jornada \(jornada) de \(totalDeJornadas)") // Jornada 2 de 3
let media = 2.5 // Double, inferido
// print(media * totalDeJornadas) // error: no se mezclan Double e Int
print(media * Double(totalDeJornadas)) // 7.5: Double(…) convierte el IntAl ejecutarlo, la consola enseña:
Jornada 2 de 3
7.5Si intentas reasignar un let, el compilador se detiene. Con let x = 3 seguido de x = 4, Xcode marca esa línea con un error y el mensaje es este (en la Terminal, xcrun swift fichero.swift lo da precedido por el fichero y la línea):
error: cannot assign to value: 'x' is a 'let' constant
note: change 'let' to 'var' to make it mutableKotlin frente a Swift
Estas son líneas del Kotlin que escribiste en Variables, tipos y operaciones, con su equivalente:
| Kotlin | Swift |
|---|---|
val nombreDelTorneo = "Copa Barrio" | let nombreDelTorneo = "Copa Barrio" |
var jornada = 1 | var jornada = 1 |
println("Torneo: $nombreDelTorneo") | print("Torneo: \(nombreDelTorneo)") |
Dos diferencias que se notan a la primera: val se escribe let, y la interpolación es \(…) en vez de $….
Reto · Ejercicio: tres formas de decir lo mismo
En tu Playground, guarda el nombre del equipo, Rayo FC, en algo que no pueda cambiar, y sus goles, empezando en 0, en algo que sí. Suma dos goles e imprime el titular con los dos datos. Después guarda una media de 2.5 y un total de 3, los dos sin poder cambiar, e imprime su producto. Lo que debe salir:
Rayo FC lleva 2 goles
7.5Luego intenta cambiar el nombre del equipo, y multiplicar la media por el total sin convertir nada. Lee los dos mensajes del compilador.
Ver solución
let equipo = "Rayo FC"
var goles = 0
goles += 1
goles += 1
print("\(equipo) lleva \(goles) goles") // Rayo FC lleva 2 goles
// equipo = "Toros" // error: cannot assign to value: 'equipo' is a 'let' constant
let media = 2.5 // Double
let total = 3 // Int
// print(media * total) // error: binary operator '*' cannot be applied to operands of type 'Double' and 'Int'
print(media * Double(total)) // 7.5: se convierte a manolet no admite una segunda asignación. Y Swift no convierte números por ti: Double(total) crea un Double a partir del Int. El mensaje es binary operator '*' cannot be applied to operands of type 'Double' and 'Int': la falta de conversión.
Paso · Crea la carpeta de trabajo y el paquete
Objetivo: la carpeta ~/Marcador-Swift con el paquete Torneo dentro, que compila y pasa su test de plantilla.
En toda la parte de Swift vas a trabajar en una sola carpeta, Marcador-Swift, dentro de tu carpeta personal. Ahí vivirá el paquete Torneo (esta lección y las siguientes del lenguaje) y, más adelante, la app de iPhone, MarcadorApp, en una carpeta propia al lado del paquete. Abre una Terminal y escribe, una orden por línea:
cd ~
mkdir Marcador-Swift
cd Marcador-Swift
mkdir Torneo
cd Torneo
pwdpwd debe terminar en /Marcador-Swift/Torneo. Si ya existía una carpeta con ese nombre, mkdir responde File exists: no pasa nada, haz cd a ella.
Ahora, dentro de Torneo, crea el esqueleto:
xcrun swift package init --type libraryswift package agrupa las órdenes que trabajan con el paquete, init crea uno nuevo y --type library pide que sea una librería (código para que lo usen otros), la más simple. Como no dices un nombre, toma el de la carpeta: Torneo. Verás esto:
Creating library package: Torneo
Creating Package.swift
Creating .gitignore
Creating Sources
Creating Sources/Torneo/Torneo.swift
Creating Tests/
Creating Tests/TorneoTests/
Creating Tests/TorneoTests/TorneoTests.swiftEscribe ls y verás Package.swift, Sources y Tests; con ls -a aparece además .gitignore, un fichero oculto de configuración que puedes ignorar. Con open . ves la carpeta en el Finder.
Compila y prueba la plantilla, antes de tocar nada, para saber que tu Swift funciona:
xcrun swift build
xcrun swift testswift build compila el paquete y termina con Build complete! y el tiempo que ha tardado (la primera vez, unos segundos). Aparece una carpeta oculta .build con lo compilado, igual que build/ en Gradle. swift test compila también los tests y los ejecuta. Su salida tiene dos partes y la primera despista:
Test Suite 'All tests' started at 2026-09-30 21:53:08.981.
Test Suite 'All tests' passed at 2026-09-30 21:53:08.982.
Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.002) seconds
Test run started.
Testing Library Version: 1501
Target Platform: arm64e-apple-macos14.0
Test example() started.
Test example() passed after 0.001 seconds.
Test run with 1 test in 0 suites passed after 0.001 seconds.Swift tiene dos librerías de tests y swift test ejecuta las dos. La primera, XCTest, es la antigua; la plantilla no la usa, y por eso dice Executed 0 tests. La segunda, Swift Testing, es la moderna y la que usa la plantilla (import Testing, @Test): su última línea es la que importa, Test run with 1 test in 0 suites passed. Los números, los símbolos del principio de cada línea y alguna línea más (como la de la plataforma) cambian de una máquina a otra. En todo el curso, el resultado que miras es esa línea final de Swift Testing; de XCTest se habla más adelante, en las pruebas de la app de iPhone.
Abre el paquete en Xcode
Xcode abre una carpeta con Package.swift como un proyecto, sin ningún fichero de proyecto aparte: lo que ves en el panel de la izquierda (el Project navigator, el navegador de proyecto) son las carpetas y ficheros del disco. Hay dos caminos: en Xcode, File → Open…, eliges la carpeta Torneo y pulsas Open; o, desde la Terminal, dentro de Torneo, xed . (Xcode editor: abre la carpeta actual en Xcode).
En el navegador deberías ver Package.swift, Sources con Torneo dentro, y Tests con TorneoTests. Para el resto de la lección, Xcode es tu editor; compilar y probar lo haces desde la Terminal, donde la salida se lee entera. Dos acciones que necesitarás en todas las lecciones de Swift:
- Crear un fichero. Clic derecho sobre la carpeta donde va (por ejemplo
Sources → Torneo) → New File from Template… → Swift File → Next → escribe el nombre y pulsa Create. Si el menú de tu versión de Xcode se llama distinto, crea el fichero desde la Terminal contouch ruta/del/Fichero.swift: queda vacío y Xcode lo muestra enseguida, porque el navegador refleja el disco. - Borrar un fichero. Clic derecho sobre él → Delete → Move to Trash. O, en la Terminal,
rm ruta/del/Fichero.swift.
En un paquete no eliges el objetivo al crear el fichero: el objetivo lo decide la carpeta donde está.
Punto de control
- Existe
~/Marcador-Swift/Torneo, conPackage.swift,SourcesyTests, tal como los creóswift package init. - Desde
~/Marcador-Swift/Torneo,xcrun swift buildtermina conBuild complete!. xcrun swift testtermina conTest run with 1 test in 0 suites passed(ignora la líneaExecuted 0 testsde XCTest). El tiempo varía según la máquina.- Tienes un Playground de Xcode y en él ejecutaste el ejercicio: la consola dice
Rayo FC lleva 2 golesy7.5. - Sabes decir qué versión de Swift ejecuta
xcrun swifty por qué escribesxcrundelante, y para qué sirvenproducts,targetsydependenciesenPackage.swift.
Comprueba lo que entendiste
5 preguntas · necesitas 4 para completar la lección
1.¿Por qué en la parte de Swift escribes
xcrun swift buildy noswift build?2.¿Qué dice la primera línea de
Package.swift,// swift-tools-version: 6.2?3.En
Package.swift, ¿qué diferencia hay entre un producto y un objetivo (target)?4.Después de
let total = 3, escribestotal = 4. ¿Qué pasa?5.Quieres usar un paquete de otra persona en
Torneo. ¿Dónde lo pides enPackage.swift?