← Biblioteca técnica
Architecture · APUNTE DE ESTUDIO

Inyección de dependencias en Swift: composición y testing

Constructor injection, límites de protocolos, composition root, dobles de pruebas y trade-offs.

Concepto y propósito

DI consiste en entregar colaboradores desde fuera en vez de crearlos dentro. Es una técnica; Dependency Inversion es el principio de depender de abstracciones. Constructor injection deja visibles las dependencias obligatorias y facilita pruebas.

Inyección por inicializador
struct Account: Sendable { let id: String }
protocol AccountsRepository: Sendable {
    func load() async throws -> [Account]
}
struct LoadAccounts {
    let repository: any AccountsRepository
    func execute() async throws -> [Account] {
        try await repository.load()
    }
}

Composition root

Construye la red de dependencias en un punto claro de la aplicación: API client, repositorio real, casos de uso y ViewModel. En proyectos pequeños, un ensamblaje explícito suele ser más legible que un contenedor de DI.

Ensamblaje de dependencias
struct APIClient: Sendable { }
struct RemoteAccountsRepository: AccountsRepository {
    let api: APIClient
    func load() async throws -> [Account] { [] }
}
func makeAccountsUseCase() -> LoadAccounts {
    LoadAccounts(repository: RemoteAccountsRepository(api: APIClient()))
}

Testing con dobles

El test double debería conservar la semántica de éxito y error del contrato real. No ocultes errores convirtiéndolos en [] si el repositorio real los propaga.

Stub que representa resultado y error
struct AccountsStub: AccountsRepository {
    let result: Result<[Account], any Error>
    func load() async throws -> [Account] { try result.get() }
}
let success = LoadAccounts(repository: AccountsStub(
    result: .success([Account(id: "A1")])
))
Estilos de inyección
EstiloVentajaRiesgo
InicializadorDependencias visibles y obligatoriasMás código en el ensamblaje
ParámetroÚtil para operaciones concretasFirmas largas
Environment o locatorAcceso cómodoDependencias implícitas
Framework DIGrafos grandesConfiguración y fallos en ejecución

Trade-offs

No crees un protocolo para cada clase sin motivo. Úsalo en límites reales o donde facilite sustituir implementaciones y probar. Un Singleton no equivale a DI: crea acceso global y puede acoplar tests. Cuida el ciclo de vida y el aislamiento de actores.

FUENTES Y DOCUMENTACIÓN

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