← Biblioteca técnica
Architecture · APUNTE DE ESTUDIO

Principios SOLID en Swift — guía práctica

Los cinco principios SOLID con explicaciones cortas y ejemplos aplicados a Swift.

S — Responsabilidad única

Una clase o módulo debería tener una única razón para cambiar. Evita mezclar peticiones a API, persistencia y presentación en un mismo ViewModel.

S — Keep fetching separate from presentation
protocol OrderReading {
    func loadOrders() async throws -> [Order]
}

struct LoadOrdersUseCase {
    let repository: any OrderReading

    func execute() async throws -> [Order] {
        try await repository.loadOrders()
    }
}

O — Abierto/cerrado

Extiende el comportamiento mediante protocolos e implementaciones nuevas sin modificar constantemente código estable. Añade métodos de pago sin un switch creciente.

O — Introduce a new implementation instead of a growing switch
protocol PaymentMethod {
    func charge(_ amount: Decimal) async throws
}

struct CardMethod: PaymentMethod {
    func charge(_ amount: Decimal) async throws {
        // Charge using the card provider.
    }
}

struct TransferMethod: PaymentMethod {
    func charge(_ amount: Decimal) async throws {
        // Charge using bank transfer.
    }
}

L — Sustitución de Liskov

Una implementación debe poder sustituir otra respetando las expectativas del contrato. Un mock que silencie errores y devuelva una lista vacía puede romper esa garantía.

LSP: mocks must preserve the error contract
struct Order { let id: String }
protocol OrderReading {
    func load() async throws -> [Order]
}
enum OrderError: Error { case unavailable }
struct FailingOrders: OrderReading {
    func load() async throws -> [Order] {
        throw OrderError.unavailable
    }
}
// Returning [] instead would hide an error expected by callers.

I — Segregación de interfaces

Evita protocolos gigantes: separa OrderReading de OrderWriting si cada consumidor necesita solo una parte de sus métodos.

ISP: small, focused consumer interfaces
struct Order { let id: String }
protocol OrderReading {
    func loadOrders() async throws -> [Order]
}
protocol OrderWriting {
    func save(_ order: Order) async throws
}
struct OrderList {
    let reader: any OrderReading
}

D — Inversión de dependencias

Las capas de alto nivel dependen de abstracciones, no de implementaciones concretas. Un caso de uso recibe el protocolo y la implementación se decide en el composition root.

DIP: high-level code depends on an abstraction
struct Order { let id: String }
protocol OrderReading {
    func loadOrders() async throws -> [Order]
}
struct LoadOrdersUseCase {
    let repository: any OrderReading
    func execute() async throws -> [Order] {
        try await repository.loadOrders()
    }
}
Resumen de SOLID
PrincipioPregunta clavePista práctica
SRP¿Cuántos motivos de cambio tiene?Separar API, persistencia e interfaz.
OCP¿Puedo ampliar sin modificar la lógica estable?Implementaciones de protocolos.
LSP¿Puedo sustituir una implementación sin romper expectativas?Respetar semántica de errores.
ISP¿Dependo de funciones que no utilizo?Protocolos pequeños.
DIP¿El dominio conoce la infraestructura concreta?Inyectar abstracciones.

Trade-off: los protocolos son útiles en límites de arquitectura y tests, pero crearlos para todas las clases añade indirectamente complejidad sin mejorar necesariamente el diseño.

FUENTES Y DOCUMENTACIÓN

Notas personales de estudio. Verifica los matices según la versión de Swift/iOS y la documentación oficial.