# Regla: Estilo de Código Swift

Esta regla es OBLIGATORIA para todo código Swift nuevo o modificado.
Swift moderno (6+) tiene un sistema de tipos y un modelo de concurrencia diseñados
para eliminar clases enteras de bugs. Usarlos correctamente no es opcional.

---

## Linters obligatorios: SwiftFormat y SwiftLint

- **SwiftFormat** aplica formato consistente automáticamente. Corre en CI como verificación bloqueante.
- **SwiftLint** detecta anti-patrones, violaciones de estilo y código peligroso.
- Configurar el build phase de Xcode para correr SwiftLint al compilar.

```bash
# Instalación vía Homebrew
brew install swiftformat swiftlint

# .swiftformat — configuración base
--indent 4
--linebreaks lf
--maxwidth 120
--wraparguments before-first
--closingparen balanced
```

```yaml
# .swiftlint.yml
opt_in_rules:
  - force_unwrapping
  - implicitly_unwrapped_optional
  - missing_docs
disabled_rules:
  - todo
excluded:
  - Pods
  - .build
line_length: 120
function_body_length:
  warning: 40
  error: 50
```

---

## let sobre var: inmutabilidad por defecto

Declarar `let` siempre que el valor no cambie. `var` solo cuando la mutación es necesaria.

```swift
// MAL — var donde el valor nunca se reasigna
var nombre = "Juan"
var total = calcularTotal(items)

// BIEN — let comunica inmutabilidad
let nombre = "Juan"
let total = calcularTotal(items)
```

---

## struct con value semantics, class solo para referencias

En Swift, `struct` es el tipo por defecto. Usar `class` solo cuando se necesita
semántica de referencia (ciclo de vida compartido, herencia obligatoria).

```swift
// BIEN — struct para modelos de datos
struct Pedido {
    let id: UUID
    let items: [ItemPedido]
    let total: Decimal
}

// BIEN — class cuando se necesita referencia compartida
final class GestorConexion {
    private var conexion: URLSession
    // El gestor se pasa por referencia a múltiples servicios
}

// MAL — class donde struct es suficiente
class UsuarioDto {
    var nombre: String
    var email: String
    init(nombre: String, email: String) { ... }
}
```

---

## Swift 6: actors para estado compartido entre tareas

Los `actor`s garantizan acceso exclusivo a su estado. Reemplaza locks, DispatchQueues
y toda sincronización manual para datos compartidos entre tareas concurrentes.

```swift
// MAL — clase con estado mutable compartido entre hilos (data race)
class Cache {
    private var datos: [String: Data] = [:]
    func guardar(_ data: Data, clave: String) { datos[clave] = data }
    func obtener(clave: String) -> Data? { datos[clave] }
}

// BIEN — actor: acceso sincronizado por el compilador
actor Cache {
    private var datos: [String: Data] = [:]
    func guardar(_ data: Data, clave: String) { datos[clave] = data }
    func obtener(clave: String) -> Data? { datos[clave] }
}

// Uso — el compilador fuerza await para accesos al actor
let cache = Cache()
await cache.guardar(data, clave: "perfil")
let perfil = await cache.obtener(clave: "perfil")
```

---

## Concurrencia estructurada: async/await sobre Combine

En código nuevo, preferir `async/await` sobre Combine.
Combine se mantiene para flujos reactivos UI complejos o cuando la base de código
ya lo usa extensamente.

```swift
// MAL — Combine para una sola operación asíncrona (sobre-ingeniería)
func obtenerUsuario(id: UUID) -> AnyPublisher<Usuario, Error> {
    URLSession.shared.dataTaskPublisher(for: url)
        .map(\.data)
        .decode(type: Usuario.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
}

// BIEN — async/await: lineal, legible, con propagación de errores tipada
func obtenerUsuario(id: UUID) async throws -> Usuario {
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(Usuario.self, from: data)
}
```

---

## guard let para early return (no if let anidados)

```swift
// MAL — pirámide de la muerte con if let anidados
func procesarPedido(datos: [String: Any]?) {
    if let datos = datos {
        if let idString = datos["id"] as? String {
            if let id = UUID(uuidString: idString) {
                // lógica principal al nivel 3 de anidamiento
            }
        }
    }
}

// BIEN — guard let: early return, lógica principal al nivel 0
func procesarPedido(datos: [String: Any]?) {
    guard let datos = datos else { return }
    guard let idString = datos["id"] as? String else { return }
    guard let id = UUID(uuidString: idString) else {
        logger.warning("ID inválido en datos de pedido")
        return
    }
    // lógica principal aquí, sin anidamiento
}
```

---

## Sendable para tipos compartidos entre actores

Swift 6 hace obligatoria la conformidad con `Sendable` para tipos que cruzan
fronteras de concurrencia. Los tipos que no son `Sendable` no pueden pasarse
entre `Task`s o actores sin una copia.

```swift
// BIEN — struct es Sendable por defecto si todos sus campos lo son
struct Pedido: Sendable {
    let id: UUID
    let total: Decimal
}

// BIEN — clase Sendable requiere sincronización interna
final class ConfiguracionGlobal: @unchecked Sendable {
    private let lock = NSLock()
    private var _configuracion: [String: String] = [:]

    func obtener(_ clave: String) -> String? {
        lock.withLock { _configuracion[clave] }
    }
}
```

---

## Trailing closures solo para el último parámetro

```swift
// MAL — trailing closure cuando no es el último parámetro
UIView.animate(withDuration: 0.3) {
    self.vista.alpha = 1.0
} completion: { _ in }

// BIEN — trailing closure solo cuando es el último argumento y mejora la legibilidad
UIView.animate(withDuration: 0.3, animations: {
    self.vista.alpha = 1.0
}) { _ in
    // completion
}

// BIEN — trailing closure natural para el último parámetro
items.filter { $0.esActivo }
     .map { $0.nombre }
```

---

## Límites de tamaño y nombres

- Funciones: máxima **40 líneas** (sin contar líneas en blanco y comentarios)
- Parámetros: máximo **4**. Si se necesitan más, usar un struct de parámetros
- Tipos (struct/class/enum): `UpperCamelCase`
- Funciones, propiedades y variables: `lowerCamelCase`
- Constantes: `lowerCamelCase` (Swift no usa SCREAMING_CASE)

---

## Checklist de estilo Swift antes de abrir PR

- [ ] SwiftFormat aplicado sin errores
- [ ] SwiftLint pasa con configuración del proyecto
- [ ] Sin `var` donde `let` es suficiente
- [ ] `struct` para modelos de datos; `class` justificado cuando se usa
- [ ] Sin `if let` anidados: usar `guard let` con early return
- [ ] Concurrencia con `async/await`; actores para estado compartido
- [ ] Sin force unwrap `!` en `src/` (solo en tests con justificación)
- [ ] Funciones <= 40 líneas
