Guía de Swift: De 0 a 100
¿Qué es Swift?
Swift es un lenguaje de programación compilado, multiparadigma y de tipado fuerte, creado por Chris Lattner en Apple y lanzado por primera vez en 2014. Utiliza LLVM como infraestructura de compilación, lo que le permite generar código nativo optimizado para los procesadores de Apple (ARM64, x86_64) y también para Linux y Windows. Swift reemplazó a Objective-C como lenguaje principal del ecosistema Apple, ofreciendo mayor seguridad de memoria, sintaxis moderna y mejor rendimiento.
¿Dónde se usa?
- iOS / iPadOS: UIKit, SwiftUI — aplicaciones nativas para iPhone y iPad
- macOS / Catalyst: AppKit, SwiftUI — apps de escritorio y Catalyst (iPad → Mac)
- watchOS: WatchKit, SwiftUI — aplicaciones para Apple Watch
- tvOS: TVML, SwiftUI — apps para Apple TV
- Server-side: Vapor, Hummingbird, Queues — APIs REST, GraphQL, WebSockets
- Scripting / CLI: SPM permite crear herramientas de línea de comandos nativas
¿Quién lo usa? Uber (migración de Objective-C a Swift), Airbnb, Lyft, Slack, Telegram, WhatsApp (iOS), LinkedIn. Empresas que necesitan rendimiento nativo en dispositivos Apple.
Philosophía del lenguaje:
- Protocol-Oriented Programming: Swift prioriza protocolos y structs sobre clases y herencia
- Safety: opcionales eliminan los null pointer exceptions, tipo fuerte evita errores de tipo en compilación
- Performance: value types evitan overhead de referencia, compilación con LLVM optimiza al máximo
- Clarity: sintaxis expresiva con nombres de parámetros externos,
guard,defer
Fuente: Swift.org — About, Wikipedia — Swift, Chris Lattner — Swift History
Prerrequisitos
Antes de empezar, necesitas:
- Conocimientos básicos de terminal: navegar directorios, ejecutar comandos, usar un editor de texto
- El lenguaje/herramienta instalado en tu sistema (sigue la sección de Instalación si aún no lo tienes)
Si no cumples algún requisito, no te preocupes: cada sección te guiará paso a paso.
¿Cómo empezar? Instalación en todos los entornos
macOS (el entorno nativo)
- Descarga Xcode desde la App Store (gratuito, ~8 GB). Incluye:
- Compilador de Swift (
swiftc) - Interface Builder (diseño visual de UIs)
- Simuladores de iOS, iPadOS, watchOS, tvOS
- Instruments (profiler)
- Swift Playgrounds interactivos
- Compilador de Swift (
- Alternativa ligera: instala solo Command Line Tools:
xcode-select --install(incluyeswiftc,lldb, sin Xcode completo) - Verifica:
swift --versionen terminal
Linux (Ubuntu/Debian)
# Descarga el toolchain de swift.org
wget https://download.swift.org/swift-6.0-release/ubuntu2404/swift-6.0-RELEASE/swift-6.0-RELEASE-ubuntu24.04.tar.gz
tar xzf swift-6.0-RELEASE-ubuntu24.04.tar.gz
sudo mv swift-6.0-RELEASE-ubuntu24.04 /usr/share/swift
echo 'export PATH=/usr/share/swift/usr/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
Dependencias necesarias:
sudo apt update
sudo apt install clang libicu-dev libcurl4-openssl-dev libssl-dev git
O usa el script oficial de instalación:
curl -sL https://swift.org/install.sh | bash
Docker
FROM swift:6.0
WORKDIR /app
COPY Package.swift ./
COPY Sources ./Sources
RUN swift build -c release
CMD [".build/release/mi-app"]
Usa swift:6.0 como imagen base. Para producción, usa multi-stage build con swift:6.0-slim como imagen final.
Windows
- Swift está disponible para Windows desde Swift 5.3+ (toolchain oficial)
- Descarga desde swift.org/download — instalador MSI
- Requiere Visual Studio 2022 con “Desktop development with C++”
- Verifica:
swift --versionen PowerShell
Homebrew (herramientas auxiliares)
brew install swiftlint swiftformat # Linter y formateador
brew install vapor # CLI de Vapor (server-side)
brew install tuist # Generación de proyectos Xcode
Alias útiles para el día a día
alias sw='swift'
alias swb='swift build'
alias swt='swift test'
alias swr='swift run'
alias spm='swift package'
alias xc='xcodebuild'
alias xct='xcodebuild test -scheme'
alias swlint='swiftlint'
alias swfmt='swiftformat'
Fuente: Swift.org — Downloads, Vapor docs, Swift on Windows
Escala de aprendizaje 0-100 (7 niveles)
Nivel 0-15: Fundamentos absolutos
Conceptos: sintaxis basica, let/var, tipos basicos, if/else, for-in, funciones, print, cadenas.
Proyecto: calculadora CLI que suma, resta, multiplica y divide con manejo de optionals.
Nivel 15-30: Estructuras de datos y funciones avanzadas
Conceptos: arrays, sets, dictionaries, closures, map/filter/reduce, optionals, guard.
Proyecto: gestor de tareas (TODO) con persistencia JSON usando Codable.
Nivel 30-45: Protocolos y diseno orientado a protocolos
Conceptos: protocolos, extensions, protocol extensions, structs con mutating, enums con valores asociados, genericos.
Proyecto: sistema de notificaciones que usa protocolos para diferentes canales (email, SMS, push).
Nivel 45-60: Testing, dependencias y concurrencia basica
Conceptos: XCTest, SPM, DispatchQueue, async/await basico, error handling con throws.
Proyecto: libreria de validacion con tests exhaustivos y documentacion.
Nivel 60-75: SwiftUI, Combine y APIs
Conceptos: SwiftUI, Combine, URLSession, Codable, property wrappers.
Proyecto: app de clima con SwiftUI que consume API REST, maneja errores.
Nivel 75-90: Actores, SwiftData, produccion
Conceptos: actors, Sendable, SwiftData/CoreData, CI/CD, profiling con Instruments.
Proyecto: app de notas con SwiftUI + SwiftData, tests, y CI configurado.
Nivel 90-100: Arquitectura y apps complejas
Conceptos: TCA (The Composable Architecture), testing de UI, performance tuning, macros.
Proyecto: app de gestion de proyectos con multiples pantallas, persistencia, networking y tests.
Primeros pasos y configuración del entorno
Si prefieres ver directamente el “Hola Mundo” en Swift, consulta El Atlas del Hola Mundo.
El REPL
Swift incluye un intérprete interactivo. Ejecuta swift en terminal:
1> print("Hola mundo")
2> let x = 42
3> x + 8
4> [1, 2, 3].map { $0 * 2 }
Para salir: :quit o Ctrl+D.
También puedes usar swift repl si el comando swift no entra en modo REPL automáticamente.
Scripts sueltos con swiftc
Swift puede compilar y ejecutar archivos individuales:
// hola.swift
func main() {
print("Hola mundo")
}
main()
Ejecuta: swift hola.swift (interpretado) o swiftc -o hola hola.swift && ./hola (compilado)
Swift Package Manager (SPM)
SPM es el gestor de dependencias oficial, integrado en el compilador:
# Crear proyecto ejecutable
swift package init --type executable
# Crear proyecto librería
swift package init --type library
# Compilar
swift build
# Ejecutar tests
swift test
# Correr el ejecutable
swift run
Estructura Package.swift
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "MiProyecto",
platforms: [
.macOS(.v14), .iOS(.v17)
],
dependencies: [
.package(url: "https://github.com/vapor/vapor", from: "4.92.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
],
targets: [
.executableTarget(
name: "MiProyecto",
dependencies: [
.product(name: "Vapor", package: "vapor"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]
),
.testTarget(
name: "MiProyectoTests",
dependencies: ["MiProyecto"]
),
]
)
Xcode Playgrounds
Perfectos para experimentar sin crear un proyecto:
- Abre Xcode → File → New → Playground
- Elige iOS o macOS blank playground
- Escribe código y ve los resultados en la barra lateral derecha
- Los playgrounds ejecutan código de forma interactiva, mostrando el historial de valores
Editores recomendados
Los IDEs y editores más usados son VSCode, JetBrains y Neovim. Extensiones y configuraciones recomendadas.
Configuración inicial del proyecto
mkdir MiProyecto && cd MiProyecto
swift package init --type executable
swift build
Archivo .swiftlint.yml básico:
disabled_rules:
- trailing_whitespace
opt_in_rules:
- empty_count
- force_unwrapping
line_length: 120
identifier_name:
min_length: 2
max_length: 40
Fuente: Swift.org — SPM, SwiftLint docs
Paradigmas de programación
Swift es un lenguaje multiparadigma con énfasis en Protocol-Oriented Programming (POP). Puedes mezclar paradigmas según el problema.
| Paradigma | Soporte en Swift |
|---|---|
| Imperativo | Statements, loops, mutabilidad con var |
| Funcional | Inmutabilidad con let, map/filter/reduce, closures |
| POO | Clases, herencia, protocolos, extensiones |
| Protocol-Oriented | Protocolos + extensiones + structs (POP) |
| Reactivo | Combine (Apple), RxSwift |
Imperativo
Swift tiene statements clásicos con sintaxis moderna:
let x = cond ? 10 : 20
for i in 0..<5 { print(i) }
var y = 0
while y < 10 { y += 1 }
Funcional
Swift adopta programación funcional con:
- Inmutabilidad por defecto:
letcrea constantes,varpara mutables - Closures como ciudadanos de primera clase:
{ (x: Int) -> Int in x * 2 } - Higher-order functions:
map,filter,reduce,compactMap,flatMap - Optionals como mónadas:
map,flatMapen Optionals
let pares = (0..<10).filter { $0 % 2 == 0 }.map { $0 * 2 }
let resultado = Optional(10).map { $0 * 2 }.flatMap { $0 > 5 ? Optional($0) : nil }
Protocol-Oriented Programming
Swift prioriza protocolos sobre herencia:
protocol Volador { func volar() }
struct Pajaro: Volador { func volar() { print("bate alas") } }
struct Avion: Volador { func volar() { print("enciende motores") } }
Reactivo (Combine)
Combine proporciona programación reactiva con publishers/subscribers:
import Combine
let publisher = Just(42)
let cancellable = publisher.sink { valor in print(valor) }
Fuente: Swift.org — Protocols, Apple — Combine
Tipos de datos y variables
Value Types vs Reference Types
¿Qué es?
Swift distingue entre value types (structs, enums) que se copian al asignarse, y reference types (classes, actors) que se comparten por referencia. Esta distinción es fundamental para entender la gestión de memoria en Swift.
🔗 Swift Book — Value and Reference Types
Sintaxis básica
// Value type: struct se copia
struct Punto { var x: Double; var y: Double }
var p1 = Punto(x: 0, y: 0)
var p2 = p1 // copia
p2.x = 10
print(p1.x) // 0 (no afecta a p1)
// Reference type: class se comparte
class Persona { var nombre: String; init(nombre: String) { self.nombre = nombre } }
let persona1 = Persona(nombre: "Alice")
let persona2 = persona1 // referencia
persona2.nombre = "Bob"
print(persona1.nombre) // "Bob" (afecta a persona1)
🧪 Cómo probarlo
import XCTest
class ValueVsReferenceTests: XCTestCase {
func testValueTypeCopy() {
struct Point { var x: Int }
var a = Point(x: 5)
var b = a
b.x = 10
XCTAssertEqual(a.x, 5)
XCTAssertEqual(b.x, 10)
}
func testReferenceTypeShared() {
class Obj { var val = 0 }
let a = Obj()
let b = a
b.val = 42
XCTAssertEqual(a.val, 42)
}
}
💡 Memoria y rendimiento
- Value types en stack: cero overhead de garbage collection
- Reference types en heap: ARC gestiona la memoria automáticamente
- Copiar structs grandes tiene coste — usa
inouto clases cuando sea necesario - Las structs son más predecibles en rendimiento (sin pausas de ARC)
✅ Buenas prácticas
- ✅ Usa structs por defecto (value types)
- ✅ Usa clases solo cuando necesites herencia o identidad
- ⚠️ Copiar structs muy grandes (> 1000 bytes) tiene coste — considera clases
- ❌ No uses clases para tipos simples (Punto, Tamaño, Fecha)
🔗 Para saber más
Optionals
¿Qué es?
Optional<T> representa un valor que puede ser nil o contener un valor de tipo T. Es la alternativa de Swift a null pointers, y elimina los null pointer exceptions en tiempo de compilación (Optional es un enum: .some(T) o .none).
Sintaxis básica
var nombre: String? = "Alice"
nombre = nil
// Optional binding
if let nombre = nombre {
print("Hola, \(nombre)")
}
// Guard
guard let nombre = nombre else { return }
// Nil-coalescing
let saludo = nombre ?? "Invitado"
// Optional chaining
let longitud = nombre?.count
// Force unwrap (solo cuando estés 100% seguro)
let valor = nombre!
🧪 Cómo probarlo
import XCTest
class OptionalsTests: XCTestCase {
func testOptionalBinding() {
let val: Int? = 42
var result = 0
if let v = val { result = v }
XCTAssertEqual(result, 42)
}
func testNilCoalescing() {
let a: Int? = nil
XCTAssertEqual(a ?? 0, 0)
}
func testOptionalChaining() {
let s: String? = "hola"
XCTAssertEqual(s?.count, 4)
}
}
💡 Memoria y rendimiento
Optional<T>ocupa 1 byte extra (discriminant) + tamaño de T- Con optimización de puntero nulo:
Optional<SomeClass>tiene el mismo tamaño queSomeClass? - Force unwrap causa panic en runtime si es nil — comprobado en compilación solo con binding
✅ Buenas prácticas
- ✅ Usa
if let/guard letpara desempaquetar optionals - ✅ Usa
??para valores por defecto - ⚠️ No uses force unwrap (!) a menos que estés absolutamente seguro
- ❌ No uses
if letinnecesariamente — consideraguard letpara early exit
🔗 Para saber más
Control de flujo y modularidad
Condicionales: if/else y guard
¿Qué es?
Swift ofrece if/else como en otros lenguajes, pero añade guard para early exit y pattern matching en condiciones con if case. guard es especialmente útil para validar condiciones y salir temprano.
Sintaxis básica
let x = 5
if x > 0 {
print("positivo")
} else if x < 0 {
print("negativo")
} else {
print("cero")
}
// guard: early exit
func procesar(nombre: String?) {
guard let nombre = nombre else { return }
print("Hola, \(nombre)")
}
// if case: pattern matching en condiciones
let valor: Int? = 42
if case .some(let v) = valor {
print(v)
}
🧪 Cómo probarlo
import XCTest
class ControlFlowTests: XCTestCase {
func testIfElse() {
let x = 5
let res = x > 0 ? "positivo" : "no positivo"
XCTAssertEqual(res, "positivo")
}
func testGuard() {
func getLength(_ s: String?) -> Int {
guard let s = s else { return 0 }
return s.count
}
XCTAssertEqual(getLength("hola"), 4)
XCTAssertEqual(getLength(nil), 0)
}
}
💡 Memoria y rendimiento
guardno tiene overhead: es equivalente a unifconreturn- El compilador optimiza ambas formas al mismo código máquina
if casese compila a una comparación de enum tan eficiente como un switch
✅ Buenas prácticas
- ✅ Usa
guardpara validaciones al inicio de funciones (golden path) - ✅ Prefiere operador ternario
? :para asignaciones condicionales simples - ⚠️ No uses
guardcuando el resto de la función depende del binding - ❌ No anides
if letprofundamente — usaguardpara aplanar
🔗 Para saber más
Loops: for-in, while, repeat-while
¿Qué es?
Swift ofrece for-in para iterar colecciones y rangos, while para bucles condicionales, y repeat-while (equivalente a do-while). Los rangos se expresan con ..< (exclusivo) y ... (inclusivo).
Sintaxis básica
// for-in con rango
for i in 0..<5 { print(i) } // 0..4
for i in 0...5 { print(i) } // 0..5
// for-in con colección
let nombres = ["Alice", "Bob", "Charlie"]
for nombre in nombres { print(nombre) }
// enumerated
for (i, nombre) in nombres.enumerated() {
print("\(i): \(nombre)")
}
// while
var n = 3
while n > 0 { n -= 1 }
// repeat-while
var m = 3
repeat { m -= 1 } while m > 0
🧪 Cómo probarlo
import XCTest
class LoopsTests: XCTestCase {
func testForRange() {
var sum = 0
for i in 0...5 { sum += i }
XCTAssertEqual(sum, 15)
}
func testWhile() {
var n = 3
var result = [Int]()
while n > 0 { result.append(n); n -= 1 }
XCTAssertEqual(result, [3, 2, 1])
}
}
💡 Memoria y rendimiento
for-incon rangos se compila a un loop óptimo (igual que C)for-incon colecciones usa el protocolo Sequence/Iteratorreversed()no crea una copia: es lazy- Los rangos son tipos valor: ocupan 2 enteros en stack
✅ Buenas prácticas
- ✅ Usa
for-inpor defecto para iterar colecciones - ✅ Usa
enumerated()cuando necesites índice y valor - ⚠️ No uses
for i in 0..<array.countsi puedes iterar directamente - ❌ No modifiques una colección mientras la iteras
🔗 Para saber más
Switch con pattern matching
¿Qué es?
switch en Swift es extremadamente poderoso: soporta pattern matching, intervalos, tuplas, where clauses, y debe ser exhaustivo.
Sintaxis básica
let valor = 2
switch valor {
case 1: print("uno")
case 2: print("dos")
case 3...10: print("varios")
default: print("muchos")
}
let punto = (0, 0)
switch punto {
case (0, 0): print("origen")
case (_, 0): print("en eje X")
case (0, _): print("en eje Y")
default: print("otro")
}
let par = (1, 1)
switch par {
case let (x, y) where x == y: print("iguales")
case let (x, y): print("\(x), \(y)")
}
🧪 Cómo probarlo
import XCTest
class SwitchTests: XCTestCase {
func testSwitchBasic() {
let x = 2
let res: String
switch x {
case 1: res = "uno"
case 2: res = "dos"
default: res = "otro"
}
XCTAssertEqual(res, "dos")
}
}
💡 Memoria y rendimiento
- switch se compila a tabla de saltos o comparaciones (como en C)
- El compilador verifica exhaustividad en compilación
- Pattern matching con enums es O(1) cuando el discriminant es pequeño
✅ Buenas prácticas
- ✅ Cubre todos los casos (el compilador lo exige para enums)
- ✅ Usa
defaultpara casos no cubiertos - ⚠️ El orden importa: la primera coincidencia se ejecuta
- ❌ No uses switch con un solo caso — prefiere
ifoguard
🔗 Para saber más
Módulos y paquetes (SPM)
¿Qué es?
El Swift Package Manager (SPM) es el sistema de compilación y gestión de dependencias oficial. Cada directorio con Package.swift es un módulo (target). SPM resuelve dependencias, compila y ejecuta tests.
Sintaxis básica
// Package.swift
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "MiLibreria",
platforms: [.macOS(.v14), .iOS(.v17)],
products: [
.library(name: "MiLibreria", targets: ["MiLibreria"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
],
targets: [
.target(name: "MiLibreria", dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]),
.testTarget(name: "MiLibreriaTests", dependencies: ["MiLibreria"]),
]
)
✅ Buenas prácticas
- ✅ Usa
swift package initpara crear nuevos paquetes - ✅ Separa en múltiples targets dentro de un mismo paquete
- ⚠️ No expongas tipos internos como
publicsin necesidad
🔗 Para saber más
Sistema de archivos (File System I/O)
FileManager
¿Qué es?
FileManager es la clase estándar de Swift para operaciones con el sistema de archivos. Proporciona métodos para leer, escribir, copiar, mover y borrar archivos y directorios. Es parte de Foundation.
Sintaxis básica
import Foundation
let fm = FileManager.default
let docs = fm.urls(for: .documentDirectory, in: .userDomainMask).first!
// Escribir archivo
let contenido = "Hola mundo"
let url = docs.appendingPathComponent("archivo.txt")
try contenido.write(to: url, atomically: true, encoding: .utf8)
// Leer archivo
let leido = try String(contentsOf: url, encoding: .utf8)
print(leido)
// Listar directorio
let items = try fm.contentsOfDirectory(at: docs, includingPropertiesForKeys: nil)
for item in items { print(item.lastPathComponent) }
// Crear directorio
try fm.createDirectory(at: docs.appendingPathComponent("datos"),
withIntermediateDirectories: true)
// Verificar existencia
if fm.fileExists(atPath: url.path) {
try fm.removeItem(at: url)
}
🧪 Cómo probarlo
import XCTest
class FileManagerTests: XCTestCase {
func testWriteAndRead() throws {
let fm = FileManager.default
let tmp = fm.temporaryDirectory.appendingPathComponent("test.txt")
try "contenido".write(to: tmp, atomically: true, encoding: .utf8)
let leido = try String(contentsOf: tmp, encoding: .utf8)
XCTAssertEqual(leido, "contenido")
try fm.removeItem(at: tmp)
}
}
💡 Memoria y rendimiento
String(contentsOf:)carga todo el archivo en memoria — no apto para archivos grandes- Para archivos grandes, usa
InputStream/OutputStreamoFileHandle FileManageres thread-safe- Las operaciones de FS son lentas (ms) — en iOS, hazlas en background
✅ Buenas prácticas
- ✅ Usa
FileManager.defaultcomo singleton - ✅ Siempre maneja errores con
try/catch - ⚠️ En iOS, los archivos están sandboxeados — solo accesibles dentro del app container
- ❌ No uses
String(contentsOf:)para archivos > 10 MB
🔗 Para saber más
Algoritmos y estructuras de datos
Ordenación con sorted()
¿Qué es?
Swift proporciona sorted() para ordenar colecciones. Retorna un nuevo array ordenado (no modifica el original). Para ordenar in-place, usa sort().
Sintaxis básica
let nums = [3, 1, 4, 1, 5]
let ordenado = nums.sorted() // [1, 1, 3, 4, 5]
// Ordenación personalizada
let items = [(3, "c"), (1, "a"), (2, "b")]
let porNumero = items.sorted { $0.0 < $1.0 }
let porLetra = items.sorted { $0.1 < $1.1 }
// Con sorted(by:)
let descendente = nums.sorted(by: >)
// Ordenación estable
struct Persona { let nombre: String; let edad: Int }
let personas = [Persona(nombre: "Alice", edad: 30),
Persona(nombre: "Bob", edad: 25)]
let porEdad = personas.sorted { $0.edad < $1.edad }
🧪 Cómo probarlo
import XCTest
class SortTests: XCTestCase {
func testSorted() {
let v = [3, 1, 4, 1, 5]
XCTAssertEqual(v.sorted(), [1, 1, 3, 4, 5])
}
func testSortedCustom() {
let v = [(3, "c"), (1, "a"), (2, "b")]
let r = v.sorted { $0.0 < $1.0 }
XCTAssertEqual(r.map { $0.0 }, [1, 2, 3])
}
}
💡 Memoria y rendimiento
sorted()usa TimSort (estable): O(n log n), O(n) memoria extrasort()(in-place) usa introsort (inestable): O(n log n), O(log n) memoria- Swift Arrays son value types:
sorted()crea una copia
✅ Buenas prácticas
- ✅ Usa
sorted()cuando necesites preservar el original - ✅ Usa
sort()para ordenar in-place cuando el original no importa - ⚠️ sorted() crea una copia — para arrays grandes, considera sort() in-place
🔗 Para saber más
Map, Filter, Reduce
¿Qué es?
map, filter y reduce son higher-order functions que permiten transformar colecciones de forma declarativa. Son la base del estilo funcional en Swift.
Sintaxis básica
let nums = [1, 2, 3, 4, 5, 6]
let pares = nums.filter { $0 % 2 == 0 } // [2, 4, 6]
let dobles = nums.map { $0 * 2 } // [2, 4, 6, 8, 10, 12]
let suma = nums.reduce(0, +) // 21
// Combinados
let resultado = nums
.filter { $0 % 2 == 0 }
.map { $0 * 10 }
.reduce(0, +) // (20 + 40 + 60) = 120
// compactMap: elimina nils
let strings = ["1", "a", "2", "b", "3"]
let numeros = strings.compactMap { Int($0) } // [1, 2, 3]
// flatMap: aplana arrays anidados
let nested = [[1, 2], [3, 4]]
let flat = nested.flatMap { $0 } // [1, 2, 3, 4]
🧪 Cómo probarlo
import XCTest
class MapFilterReduceTests: XCTestCase {
func testMap() {
XCTAssertEqual([1, 2, 3].map { $0 * 2 }, [2, 4, 6])
}
func testFilter() {
XCTAssertEqual([1, 2, 3, 4].filter { $0 % 2 == 0 }, [2, 4])
}
func testReduce() {
XCTAssertEqual([1, 2, 3, 4, 5].reduce(0, +), 15)
}
func testCompactMap() {
let input = ["1", "a", "2"]
XCTAssertEqual(input.compactMap { Int($0) }, [1, 2])
}
}
💡 Memoria y rendimiento
- map/filter/reduce son lazy si usas
LazySequence(array.lazy.map { ... }) - Sin lazy, cada operación crea un nuevo array (coste de memoria)
- Para cadenas largas, usa
lazypara evitar arrays intermedios - reduce es eficiente: una sola pasada
✅ Buenas prácticas
- ✅ Prefiere map/filter sobre loops manuales con lógica simple
- ✅ Usa
compactMappara transformaciones que pueden fallar - ⚠️ Para cadenas largas (> 10K elementos), considera lazy
- ❌ No abuses de reduce para operaciones simples — usa
sum()ojoined()
🔗 Para saber más
Big O de colecciones estándar
| Operación | Array | Set | Dictionary |
|---|---|---|---|
| Acceso | O(1) | — | O(1)* |
| Búsqueda | O(n) | O(1)* | O(1)* |
| Inserción | O(1)** | O(1)* | O(1)* |
| Eliminación | O(n) | O(1)* | O(1)* |
| Ordenación | O(n log n) | — | — |
*Amortizado, asumiendo buen hash **Amortizado O(1); O(n) si necesita redimensionar
🔗 Para saber más
Conceptos clave del lenguaje
Protocolos y Protocol Extensions
¿Qué es?
Los protocolos son la herramienta fundamental de abstracción en Swift (equivalentes a interfaces en otros lenguajes). Las protocol extensions permiten proporcionar implementaciones por defecto. Esta combinación es la base del Protocol-Oriented Programming (POP).
Sintaxis básica
protocol Volador {
var velocidadMaxima: Double { get }
func volar()
}
extension Volador {
func volar() { print("Volando a \(velocidadMaxima) km/h") }
}
struct Pajaro: Volador {
let velocidadMaxima: Double = 60
}
struct Avion: Volador {
let velocidadMaxima: Double = 900
}
let p = Pajaro()
p.volar() // "Volando a 60 km/h"
🧪 Cómo probarlo
import XCTest
class ProtocolTests: XCTestCase {
protocol Saludable { func saludar() -> String }
extension Saludable { func saludar() -> String { "Hola" } }
struct Persona: Saludable {}
func testProtocolExtension() {
let p = Persona()
XCTAssertEqual(p.saludar(), "Hola")
}
}
💡 Memoria y rendimiento
- Protocolos con
protocol(keyword) se despachan dinámicamente (vtable) - Protocolos con
staticrequirements usan dispatch directo extensionde protocolos proporciona implementación por defecto sin overhead- Structs que conforman protocolos no tienen overhead adicional
✅ Buenas prácticas
- ✅ Prefiere protocolos sobre clases base para abstracción
- ✅ Usa protocol extensions para implementaciones por defecto
- ⚠️ No pongas requirements en el protocolo que no necesiten ser sobrescritos
- ❌ No uses herencia de clases cuando un protocolo + struct es suficiente
🔗 Para saber más
Structs avanzadas: mutating, computed properties, property observers
¿Qué es?
Las structs en Swift soportan mucho más que simples datos: computed properties (propiedades calculadas), property observers (willSet/didSet), mutating methods (métodos que modifican la struct), y lazy properties.
Sintaxis básica
struct Rectangulo {
var ancho: Double
var alto: Double
// Computed property
var area: Double { ancho * alto }
// Property observer
var color: String = "blanco" {
willSet { print("va a cambiar a \(newValue)") }
didSet { print("cambio de \(oldValue) a \(color)") }
}
// Mutating method
mutating func escalar(_ factor: Double) {
ancho *= factor
alto *= factor
}
// Lazy property (se calcula al primer acceso)
lazy var descripcion: String = "\(ancho)x\(alto)"
}
🧪 Cómo probarlo
import XCTest
class StructTests: XCTestCase {
func testComputedProperty() {
var r = Rectangulo(ancho: 10, alto: 20)
XCTAssertEqual(r.area, 200)
}
func testMutating() {
var r = Rectangulo(ancho: 10, alto: 20)
r.escalar(2)
XCTAssertEqual(r.ancho, 20)
XCTAssertEqual(r.alto, 40)
}
}
🔗 Para saber más
Enums con valores asociados y pattern matching
¿Qué es?
Los enums en Swift son algebraicos: cada caso puede llevar valores asociados (tuplas) o raw values. Combinados con pattern matching, son la herramienta más poderosa para modelar estados.
Sintaxis básica
enum Resultado {
case exito(Int)
case error(String)
}
// Valores asociados
enum Mensaje {
case texto(String)
case coordenadas(x: Double, y: Double)
case cerrar
}
let msg = Mensaje.texto("Hola")
switch msg {
case .texto(let s): print(s)
case .coordenadas(let x, let y): print("\(x), \(y)")
case .cerrar: print("adios")
}
// Raw values
enum Estado: String {
case activo = "activo"
case inactivo = "inactivo"
}
print(Estado.activo.rawValue) // "activo"
🧪 Cómo probarlo
import XCTest
class EnumTests: XCTestCase {
enum Opcion { case entero(Int); case texto(String) }
func testEnumPatternMatching() {
let val = Opcion.entero(42)
var result = 0
if case .entero(let v) = val { result = v }
XCTAssertEqual(result, 42)
}
func testEnumRawValue() {
enum Color: String { case rojo = "rojo" }
XCTAssertEqual(Color.rojo.rawValue, "rojo")
}
}
💡 Memoria y rendimiento
- El tamaño del enum es el del caso más grande + discriminant
- Enum con
Intraw value ocupa el tamaño del entero - Pattern matching en enums es tan eficiente como un switch en C
- Los valores asociados se almacenan inline
✅ Buenas prácticas
- ✅ Usa enums para modelar estados finitos y opciones
- ✅ Prefiere enums con valores asociados sobre optionals múltiples
- ⚠️ No añadas casos sin valores asociados si el caso necesita datos
- ❌ No uses enums para grupos de constantes — prefiere structs con static let
🔗 Para saber más
Closures
¿Qué es?
Las closures son bloques de código autónomos que pueden capturar referencias del contexto. Son equivalentes a lambdas en otros lenguajes. Swift tiene tres tipos: globales, anidadas y closure expressions.
Sintaxis básica
// Closure expression
let multiplicar = { (a: Int, b: Int) -> Int in a * b }
print(multiplicar(3, 4)) // 12
// Trailing closure
let nums = [1, 2, 3, 4]
let pares = nums.filter { $0 % 2 == 0 }
// Capturing values
func makeIncrementer(increment: Int) -> () -> Int {
var total = 0
return {
total += increment
return total
}
}
let inc = makeIncrementer(increment: 5)
print(inc()) // 5
print(inc()) // 10
// @escaping: closure que escapa la función
func ejecutarDespues(_ closure: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
}
}
🧪 Cómo probarlo
import XCTest
class ClosureTests: XCTestCase {
func testClosure() {
let sum = { (a: Int, b: Int) -> Int in a + b }
XCTAssertEqual(sum(2, 3), 5)
}
func testCapturing() {
func makeCounter() -> () -> Int {
var count = 0
return { count += 1; return count }
}
let counter = makeCounter()
XCTAssertEqual(counter(), 1)
XCTAssertEqual(counter(), 2)
}
}
💡 Memoria y rendimiento
- Las closures son objetos en heap: ocupan memoria por las variables capturadas
- Sin captura: son punteros a función (sin overhead)
@escapingimplica retención potencial (ARC)- Los closures con captura débil (
[weak self]) evitan retain cycles
✅ Buenas prácticas
- ✅ Usa trailing closures para APIs que reciben closures como último parámetro
- ✅ Usa
$0,$1para closures simples (shorthand argument names) - ⚠️ Captura
[weak self]en closures que puedan crear retain cycles - ❌ No captures objetos grandes innecesariamente en closures
🔗 Para saber más
Error handling: throws, try/catch, Result, defer
¿Qué es?
Swift maneja errores con throws/try/catch, que son más seguros que excepciones porque el compilador verifica que los errores se manejen. defer ejecuta código al salir del scope (para limpieza). Result<T, E> es un enum para errores como valores.
Sintaxis básica
enum MiError: Error {
case noEncontrado
case redError(String)
}
func leerArchivo(_ path: String) throws -> String {
guard !path.isEmpty else { throw MiError.noEncontrado }
return "contenido"
}
// try? (opcional)
let resultado = try? leerArchivo("")
// try! (force, puede crash)
// let contenido = try! leerArchivo("test.txt")
// do/catch
do {
let contenido = try leerArchivo("test.txt")
print(contenido)
} catch MiError.noEncontrado {
print("no encontrado")
} catch {
print("error: \(error)")
}
// defer
func procesar() throws {
let file = open(...)
defer { close(file) } // se ejecuta al salir
// ... usar file
}
// Result<T, E>
func dividir(_ a: Int, _ b: Int) -> Result<Int, Error> {
guard b != 0 else { return .failure(MiError.redError("div por cero")) }
return .success(a / b)
}
🧪 Cómo probarlo
import XCTest
class ErrorTests: XCTestCase {
enum SimpleError: Error { case fallo }
func testThrows() throws {
func puedeFallar(_ x: Int) throws -> Int {
if x < 0 { throw SimpleError.fallo }
return x * 2
}
let r = try puedeFallar(5)
XCTAssertEqual(r, 10)
XCTAssertThrowsError(try puedeFallar(-1))
}
func testResult() {
let r: Result<Int, Error> = .success(42)
XCTAssertEqual(try? r.get(), 42)
}
}
🔗 Para saber más
Genéricos, where constraints, opaque types (some), any (existential)
¿Qué es?
Los genéricos permiten escribir código reutilizable con cualquier tipo. some (opaque types) oculta el tipo concreto pero mantiene la identidad. any (existential types) permite heterogeneidad con boxed protocol types.
Sintaxis básica
// Función genérica
func elMayor<T: Comparable>(_ a: T, _ b: T) -> T {
a > b ? a : b
}
// Struct genérica
struct Par<T, U> { let primero: T; let segundo: U }
// Where constraints
func sonIguales<T: Equatable, U: Equatable>(_ a: T, _ b: U) -> Bool where T == U {
a == b
}
// Opaque type (some)
func crearNumero() -> some Equatable { 42 }
let x = crearNumero()
// let y = crearNumero() == 42 // Sí, porque ambos son algún Equatable
// Existential type (any)
let valores: [any Equatable] = [1, "hola", true]
🧪 Cómo probarlo
import XCTest
class GenericTests: XCTestCase {
func testGenericFunction() {
func id<T>(_ x: T) -> T { x }
XCTAssertEqual(id(42), 42)
XCTAssertEqual(id("hola"), "hola")
}
func testOpaqueType() {
func make() -> some Equatable { 42 }
XCTAssertEqual(make() as! Int, 42)
}
}
✅ Buenas prácticas
- ✅ Usa genéricos para código reutilizable sin perder tipo
- ✅ Usa
somepara ocultar tipos concretos en APIs públicas - ⚠️
anytiene overhead de boxed — úsalo solo cuando necesites heterogeneidad
🔗 Para saber más
Codable (Encodable/Decodable)
¿Qué es?
Codable es un protocolo que combina Encodable y Decodable. Convierte tipos Swift a/desde formatos externos (JSON, Property List, etc.) automáticamente con Codable + JSONEncoder/JSONDecoder.
Sintaxis básica
struct Usuario: Codable {
let nombre: String
let edad: Int
let email: String
}
// Codificar a JSON
let usuario = Usuario(nombre: "Alice", edad: 30, email: "alice@ex.com")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(usuario)
let jsonString = String(data: jsonData, encoding: .utf8)!
// Decodificar desde JSON
let decoder = JSONDecoder()
let decoded = try decoder.decode(Usuario.self, from: jsonData)
🧪 Cómo probarlo
import XCTest
class CodableTests: XCTestCase {
struct Item: Codable, Equatable { let id: Int; let name: String }
func testEncodeDecode() throws {
let original = Item(id: 1, name: "test")
let data = try JSONEncoder().encode(original)
let decoded = try JSONDecoder().decode(Item.self, from: data)
XCTAssertEqual(original, decoded)
}
func testCustomKeys() throws {
struct Product: Codable {
let id: Int
let productName: String
enum CodingKeys: String, CodingKey {
case id
case productName = "product_name"
}
}
let json = "{"id": 1, "product_name": "Widget"}".data(using: .utf8)!
let product = try JSONDecoder().decode(Product.self, from: json)
XCTAssertEqual(product.productName, "Widget")
}
}
🔗 Para saber más
async/await, actors, Sendable
¿Qué es?
Swift Concurrency (iOS 13+/macOS 10.15+) proporciona async/await nativo, actors para estado protegido, y Sendable para transferencia segura entre concurrencias. Es el modelo moderno de concurrencia en Swift.
Sintaxis básica
// async/await
func fetchUser(id: Int) async throws -> Usuario {
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(Usuario.self, from: data)
}
// Actor: protege estado compartido
actor Contador {
private var valor = 0
func incrementar() { valor += 1 }
func obtener() -> Int { valor }
}
let contador = Contador()
await contador.incrementar()
print(await contador.obtener())
// Sendable: tipos seguros para concurrencia
struct Configuracion: Sendable {
let timeout: Int
}
🧪 Cómo probarlo
import XCTest
class AsyncTests: XCTestCase {
func testAsyncFunction() async {
async func obtener() -> Int { 42 }
let result = await obtener()
XCTAssertEqual(result, 42)
}
func testActor() async {
actor Accumulator {
var value = 0
func add(_ x: Int) { value += x }
func get() -> Int { value }
}
let acc = Accumulator()
await acc.add(10)
await acc.add(20)
XCTAssertEqual(await acc.get(), 30)
}
}
💡 Memoria y rendimiento
- async/await es cooperativo: cada tarea usa ~1 KB de overhead
- Actors protegen el estado con un lock interno (serial executor)
- Sendable es una restricción de compilación: cero overhead
- TaskGroup permite paralelismo estructurado
✅ Buenas prácticas
- ✅ Usa async/await para operaciones de red y E/S
- ✅ Usa actors para proteger estado mutable compartido
- ⚠️ No llames código bloqueante dentro de contextos async
- ❌ No uses DispatchQueue cuando async/await es suficiente
🔗 Para saber más
POO y patrones de diseño
Clases y herencia
¿Qué es?
Swift soporta POO clásica con clases, herencia, overriding y polimorfismo. Sin embargo, el lenguaje prioriza protocolos y structs sobre clases. Usa clases solo cuando necesites herencia o identidad por referencia.
Sintaxis básica
class Animal {
let nombre: String
init(nombre: String) { self.nombre = nombre }
func sonido() -> String { "" }
}
class Perro: Animal {
override func sonido() -> String { "Guau" }
}
class Gato: Animal {
override func sonido() -> String { "Miau" }
}
let animales: [Animal] = [Perro(nombre: "Rex"), Gato(nombre: "Misu")]
for animal in animales {
print(animal.sonido()) // polimorfismo dinámico
}
Patrones de diseño
| Patrón | En Swift |
|---|---|
| Strategy | Protocolos + structs |
| Observer | Combine (Publisher/Subscriber) |
| Singleton | static let shared = ... + private init |
| Factory | Protocolo + registry |
| Builder | Result Builders o métodos encadenados |
| Adapter | Protocol conformance |
Builder con Result Builders
@resultBuilder
struct StringBuilder {
static func buildBlock(_ parts: String...) -> String {
parts.joined(separator: " ")
}
}
@StringBuilder func saludo() -> String {
"Hola"
"mundo"
"Swift"
}
print(saludo()) // "Hola mundo Swift"
Singleton
class ConfigManager {
static let shared = ConfigManager()
private init() {} // impide crear otras instancias
var timeout: Int = 30
}
Polimorfismo en detalle
Polimorfismo por protocolos (POP)
¿Qué es?
POP (Protocol-Oriented Programming) es el polimorfismo preferido en Swift. Los protocolos definen interfaces, y las extensions proporcionan implementaciones por defecto. Cualquier tipo (struct, enum, class) puede conformar protocolos.
Sintaxis básica
protocol Describible {
var descripcion: String { get }
}
extension Describible {
var descripcion: String { "Algo" }
}
struct Persona: Describible {
let nombre: String
var descripcion: String { "Persona: \(nombre)" }
}
struct Cosa: Describible {}
let items: [any Describible] = [Persona(nombre: "Alice"), Cosa()]
for item in items { print(item.descripcion) }
Polimorfismo por herencia (clásico)
class Figura {
func area() -> Double { 0 }
}
class Circulo: Figura {
let radio: Double
init(radio: Double) { self.radio = radio }
override func area() -> Double { Double.pi * radio * radio }
}
Polimorfismo con genéricos y where constraints
func areaTotal<T: Figura>(_ figuras: [T]) -> Double {
figuras.reduce(0) { $0 + $1.area() }
}
Polimorfismo con Opaque Types (some)
func crearForma() -> some Figura { Circulo(radio: 5) }
Interacción con contenido multimedia
Procesamiento de imágenes
¿Qué es?
Swift tiene soporte nativo para imágenes a través de UIImage (iOS) / NSImage (macOS) y Core Image para filtros.
Sintaxis básica
import UIKit
// Cargar imagen
guard let imagen = UIImage(named: "foto") else { return }
// Redimensionar
let nuevoTamano = CGSize(width: 800, height: 600)
let renderer = UIGraphicsImageRenderer(size: nuevoTamano)
let redimensionada = renderer.image { _ in
imagen.draw(in: CGRect(origin: .zero, size: nuevoTamano))
}
// Filtros con Core Image
let ciImagen = CIImage(image: "img/guia_0_100_swift/guia_0_100_swift_cover-1200.webp"
let filtro = CIFilter(name: "CISepiaTone")
filtro?.setValue(ciImagen, forKey: kCIInputImageKey)
filtro?.setValue(0.8, forKey: kCIInputIntensityKey)
let resultado = filtro?.outputImage
🔗 Para saber más
Bases de datos
CoreData
¿Qué es?
CoreData es el framework de persistencia de Apple. Es un ORM graph-based que maneja el ciclo de vida de objetos, relaciones, y persistencia (SQLite por defecto). Ideal para aplicaciones iOS/macOS complejas.
SwiftData (moderno, Swift native)
¿Qué es?
SwiftData (iOS 17+/macOS 14+) es el reemplazo moderno de CoreData. Usa macros de Swift para definir el esquema de datos y @Model para marcar clases persistibles.
Fluent (Vapor ORM)
¿Qué es?
Fluent es el ORM de Vapor para Swift server-side. Soporta PostgreSQL, MySQL, SQLite. Define modelos con protocolos y migraciones programáticas.
WebSockets
URLSessionWebSocketTask
¿Qué es?
URLSessionWebSocketTask es la API nativa de WebSocket en iOS 13+/macOS 10.15+. Permite comunicación bidireccional con el servidor usando URLSession.
Sintaxis básica
import Foundation
let url = URL(string: "wss://echo.websocket.org")!
let session = URLSession.shared
let ws = session.webSocketTask(with: url)
// Enviar mensaje
ws.send(.string("Hola servidor")) { error in
if let error = error { print(error) }
}
// Recibir mensaje
ws.receive { result in
switch result {
case .success(let mensaje):
switch mensaje {
case .string(let texto): print(texto)
case .data(let datos): print(datos)
@unknown default: break
}
case .failure(let error): print(error)
}
}
ws.resume()
🔗 Para saber más
Concurrencia y paralelismo
async/await (concurrencia estructurada moderna)
¿Qué es?
La concurrencia moderna de Swift con async/await, TaskGroup y structured concurrency. Es el modelo recomendado para todo código nuevo.
Sintaxis básica
// TaskGroup: paralelismo estructurado
func procesarMultiples() async throws -> [String] {
try await withThrowingTaskGroup(of: String.self) { group in
for id in 1...5 {
group.addTask { await fetchUser(id: id).nombre }
}
var resultados: [String] = []
for try await resultado in group {
resultados.append(resultado)
}
return resultados
}
}
// Task: unidad de trabajo asíncrono
Task {
let result = await fetchUser(id: 1)
print(result)
}
Actors
¿Qué es?
Los actors protegen el estado mutable compartido con un serial executor. Garantizan que solo una tarea acceda al estado a la vez.
Combine
¿Qué es?
Combine (Apple, iOS 13+) es un framework reactivo que proporciona Publishers, Subscribers y Operators para manejar flujos de datos asíncronos.
Tabla de decisión: async/await vs Combine vs DispatchQueue
| async/await | Combine | DispatchQueue | |
|---|---|---|---|
| Propósito | Operaciones asíncronas | Flujos de datos reactivos | Tareas en cola |
| Complejidad | Baja | Media | Baja-media |
| Cancelación | Task.cancel() | AnyCancellable | N/A |
| Testing | async XCTest | TestScheduler | XCTestExpectation |
| Uso típico | Red, BD, E/S | UI, notificaciones | Tareas en background |
Testing y calidad
Frameworks de testing
Swift tiene XCTest como framework nativo de testing. Otras herramientas incluyen Quick/Nimble (BDD) y Swift Testing (Swift 6+).
| Framework | Proposito | Async? | CLI |
|---|---|---|---|
| XCTest | Testing nativo de Apple | Si (async XCTest) | swift test |
| Quick + Nimble | BDD (Behavior Driven Dev) | Si | swift test |
| Swift Testing | Moderno (Swift 6+, swift-testing) | Si | swift test |
Sintaxis basica
import XCTest
final class CalculadoraTests: XCTestCase {
func testSuma() {
let resultado = 2 + 2
XCTAssertEqual(resultado, 4)
}
func testDivisionPorCero() {
func dividir(_ a: Int, _ b: Int) -> Int? {
guard b != 0 else { return nil }
return a / b
}
XCTAssertNil(dividir(10, 0))
XCTAssertEqual(dividir(10, 2), 5)
}
func testAsyncOperation() async {
async func obtener() -> Int { 42 }
let resultado = await obtener()
XCTAssertEqual(resultado, 42)
}
func testPerformance() {
measure {
_ = (0..<1000).map { $0 * 2 }
}
}
}
💡 Memoria y rendimiento
- XCTest ejecuta tests en paralelo por clase por defecto
- Cada test case se ejecuta en instancia separada de XCTestCase
measureejecuta el bloque 10 veces y promedia el tiempo
✅ Buenas practicas
- ✅ Escribe tests para toda funcion publica y logica critica
- ✅ Usa
setUp/tearDownpara configuracion/limpieza repetitiva - ⚠️ No tests propiedad privada directamente
- ❌ No escribas tests que dependan del orden de ejecucion
🔗 Para saber mas
Conceptos avanzados
Property Wrappers
¿Que es?
Los Property Wrappers anaden logica reutilizable a las propiedades. Se definen con @propertyWrapper.
@propertyWrapper
struct Minimo<T: Comparable> {
private var valor: T
private let minimo: T
init(wrappedValue: T, minimo: T) {
self.valor = wrappedValue
self.minimo = minimo
}
var wrappedValue: T {
get { valor }
set { valor = max(newValue, minimo) }
}
}
struct Configuracion {
@Minimo(minimo: 0) var timeout: Int = 30
@Minimo(minimo: 1) var intentos: Int = 3
}
Macros (Swift 5.9+)
Las macros (Swift 5.9+) permiten generar codigo en compilacion. @Observable, @Model (SwiftData) son macros.
SwiftUI
SwiftUI es el framework declarativo de UI de Apple.
import SwiftUI
struct ContentView: View {
@State private var contador = 0
var body: some View {
VStack {
Text("Contador: \(contador)")
.font(.title)
Button("Incrementar") {
contador += 1
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
}
}
🔗 Para saber mas
Proyecto final integrador: Procesador de logs
Un procesador de logs que combina file system, algoritmos, protocolos, testing y concurrencia.
import Foundation
enum Nivel: String, Codable {
case info = "INFO"
case warn = "WARN"
case error = "ERROR"
}
struct LogEntry: Codable {
let nivel: Nivel
let mensaje: String
let linea: Int
let timestamp: Date
}
protocol LogProcessor {
func procesar(archivo: URL) throws -> [LogEntry]
func reportar(entries: [LogEntry]) -> [Nivel: Int]
}
class SimpleLogProcessor: LogProcessor {
func procesar(archivo: URL) throws -> [LogEntry] {
let contenido = try String(contentsOf: archivo, encoding: .utf8)
let lineas = contenido.split(separator: "\n")
return lineas.enumerated().compactMap { (i, linea) in
let partes = linea.split(separator: ":", maxSplits: 1)
guard partes.count == 2,
let nivel = Nivel(rawValue: String(partes[0]).trimmingCharacters(in: .punctuationCharacters)) else {
return nil
}
return LogEntry(nivel: nivel, mensaje: String(partes[1]).trimmingCharacters(in: .whitespaces),
linea: i + 1, timestamp: Date())
}
}
func reportar(entries: [LogEntry]) -> [Nivel: Int] {
Dictionary(grouping: entries, by: { $0.nivel }).mapValues { $0.count }
}
}
Tests
import XCTest
final class LogProcessorTests: XCTestCase {
func testProcesarArchivo() throws {
let lineas = ["[INFO]: inicio", "[ERROR]: fallo"]
let contenido = lineas.joined(separator: "\n")
let url = FileManager.default.temporaryDirectory
.appendingPathComponent("test.log")
try contenido.write(to: url, atomically: true, encoding: .utf8)
let processor = SimpleLogProcessor()
let entries = try processor.procesar(archivo: url)
XCTAssertEqual(entries.count, 2)
XCTAssertEqual(entries[0].nivel, .info)
XCTAssertEqual(entries[1].nivel, .error)
let report = processor.reportar(entries: entries)
XCTAssertEqual(report[.info], 1)
XCTAssertEqual(report[.error], 1)
try FileManager.default.removeItem(at: url)
}
}
Despliegue a producción
Build para producción
swift build -c release
Docker multi-stage
# Stage 1: Build
FROM swift:6.0 AS build
WORKDIR /app
COPY Package.swift ./
RUN swift package resolve
COPY . .
RUN swift build -c release --static-swift-stdlib
# Stage 2: Runtime
FROM ubuntu:24.04
WORKDIR /app
RUN apt-get update && apt-get install -y libicu-dev libxml2 && rm -rf /var/lib/apt/lists/*
COPY --from=build /app/.build/release /app
EXPOSE 8080
CMD ["./App"]
Hosting
CI/CD (GitHub Actions)
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: swift-actions/setup-swift@v2
- run: swift build -c release
Canales y recursos en espanol
YouTube
- SwiftBeta — iOS, Swift, SwiftUI
- HolaMundo — Swift, programacion general
- Codigo con Juan — Swift, iOS, SwiftUI
- iOS Development — iOS en espanol
- Escuela iOS — formacion completa iOS
Comunidades
- r/swift — comunidad global
- Swift en Espanol (Telegram)
- iOS Developers (Discord)
- Swift.org Forums
Repositorios GitHub
- awesome-ios
- Swift Style Guide
- Alamofire — networking HTTP
Hacks y tips de productividad
1. swift test —filter
swift test --filter ProcesadorTests
2. Code snippets en Xcode
Crea snippets reutilizables: arrastra codigo al Code Snippet Library.
3. Debugging con LLDB
po variable # print object
p variable # print value
4. Instruments para profiling
- Time Profiler: detecta cuellos de botella en CPU
- Allocations: rastrea fugas de memoria
- Leaks: detecta retain cycles
5. Property Wrappers personalizados
Usalos para validacion, normalizacion o transformacion automatica.
6. Result Builders para DSLs
Crea DSLs con @resultBuilder para construir estructuras complejas.
7. swift-format
brew install swift-format
swift-format format --in-place Sources/
8. Package plugins
SPM soporta plugins (Swift 5.6+) para generar codigo, formatear, lintar.
9. Any vs some vs any (existential)
- Usa
somepara ocultar tipos concretos con dispatch estatico - Usa
anypara heterogeneidad con boxed protocol types - Usa genericos para maximo rendimiento
10. Codable personalizado con CodingKeys
struct Product: Codable {
let id: Int
let name: String
enum CodingKeys: String, CodingKey {
case id
case name = "product_name"
}
}
11. @MainActor para UI
Marca funciones y tipos con @MainActor para garantizar que el codigo de UI se ejecute en el hilo principal.
12. Swift Testing (Swift 6+)
import Testing
@Test func suma() {
#expect(2 + 2 == 4)
}
⚠️ Errores comunes
Si algo falla, lo más probable es que esté en esta tabla:
| Error | Causa | Solución |
|---|---|---|
Cannot find 'X' in scope | Variable, función o tipo no declarado, o falta import | Añade el import del framework/módulo; revisa la ortografía |
Value of optional type 'X?' must be unwrapped | Accedes a un valor opcional sin desempaquetar | Usa if let, guard let, ?? (nil-coalescing) o !.unwrap (solo si estás seguro) |
'init(coder:)' has not been implemented | Una subclase de UIView / UIViewController no implementa el initializer requerido | Añade required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } |
Generic parameter 'X' could not be inferred | El compilador no puede deducir el tipo genérico desde los argumentos | Especifica el tipo explícicamente: funciona<T>(valor: T) o pasa el tipo como argumento |
No such module 'X' (en Xcode) | Falta importar el framework o el paquete SPM/CocoaPods no está integrado | Verifica que el paquete está en File > Add Package Dependencies; haz pod install si usas CocoaPods |
Expression was too complex to be solved in reasonable time | Una expresión con many anidaciones supera el límite del tipo-checker | Divide la expresión en variables intermedias o extrae funciones auxiliares |
Variable with the same name is being used in a different capture | Cierres (closures) capturan variables con el mismo nombre en scopes distintos | Renombra las variables o usa un nombre único en cada closure |
🔗 Guías relacionadas
- Java de 0 a 100 — otro lenguaje de tipado fuerte con POO: Guía de Java: De 0 a 100
- C# de 0 a 100 — comparable en concurrencia y ecosistema: Guía de C# y .NET: De 0 a 100
- Docker — containeriza servicios backend Swift/Vapor: Docker: instalación y primeros pasos
Referencias y documentacion oficial
- Swift.org (oficial)
- Swift Book
- Apple Developer Documentation
- Swift by Sundell
- Hacking with Swift
- NSHipster
- raywenderlich.com
- Swift Evolution
- Apple WWDC Videos
- Swift Package Index
- Vapor docs
- SwiftLint docs
- Xcode Instruments Guide
- SwiftUI Tutorials
- The Swift Programming Language
- Swift Algorithms
- Swift Collections
- Swift Async Algorithms
- Kodeco (formerly RW)
- Awesome Swift
