A medida que crece una app de iOS, es normal que la gestión de dependencias se convierta en un quebradero de cabeza.
Tarde o temprano aparece el patrón Swift Service Locator. A algunos les resulta práctico, mientras que otros lo rechazan por considerarlo un antipatrón.
Me ha funcionado mejor aplicarlo de forma selectiva, solo en los puntos donde realmente hacía falta, en lugar de adoptarlo por completo.
La conclusión es la siguiente.
Service Locator no es una alternativa completa a DI (Dependency Injection, inyección de dependencias); si se usa mal, se convierte en un antipatrón que oculta las dependencias. Se parece más a una herramienta auxiliar.
Hoy explicaré qué es este patrón, por qué recibe críticas y cuándo puede seguir siendo útil, basándome en mi experiencia.
¿Qué es el patrón Service Locator?
En una frase, es una forma de obtener los objetos necesarios desde un registro central.
Se coloca un único «registro» en algún punto y se buscan allí los objetos cada vez que hacen falta.
Si Constructor Injection introduce las dependencias desde fuera, Service Locator las obtiene directamente desde dentro.
Con el código, la idea se entiende enseguida.
// Estructura que registra y obtiene servicios desde un registro central
final class ServiceLocator {
static let shared = ServiceLocator()
private var services: [String: Any] = [:]
func register<T>(_ service: T) { services["\(T.self)"] = service }
func resolve<T>() -> T { services["\(T.self)"] as! T }
}
Se registran una vez al iniciar la app.
El código que los utiliza obtiene las dependencias necesarias de esta forma.
// El modelo de vista «busca» directamente sus dependencias
final class FeedViewModel {
private let api: APIClient = ServiceLocator.shared.resolve()
// Funciona sin inyectar api en el constructor
}
Como puedes ver, la principal ventaja es que el constructor queda más limpio.
¿Por qué se considera un antipatrón Service Locator?
La razón principal es una sola: las dependencias quedan ocultas.
Volvamos a ver el código FeedViewModelde arriba. Solo mirando el constructor, no se puede saber que esta clase usa APIClient.
Solo se descubre al abrir la implementación. En un equipo, esto resulta bastante incómodo.
El segundo problema son las pruebas.
Con Constructor Injection basta con pasar un Mock durante las pruebas. Con Service Locator hay que cambiar el estado del registro global, por lo que las pruebas pueden interferir fácilmente entre sí.
El tercero es el riesgo en tiempo de ejecución.
Si intentas obtener una dependencia que no se registró, la app falla durante la ejecución, no en compilación. El casting forzado as! del ejemplo anterior es exactamente el punto problemático.
En resumen:
| Criterio de comparación | Constructor Injection (DI) | Service Locator |
|---|---|---|
| Exposición de dependencias | Se muestra claramente | Oculta internamente |
| Facilidad de prueba | Alta | Más bien baja |
| Momento en que se detectan errores | Compilación | Ejecución |
| Concisión del constructor | Más argumentos | Limpio |
Entonces, ¿cuándo está bien usarlo?
No es necesariamente malo. Me resultó útil en situaciones como estas.
- Un único servicio compartido en toda la app, como un logger o una herramienta de análisis
- Cuando la ruta de Constructor Injection es demasiado profunda y los argumentos se siguen propagando
- Una etapa de transición al introducir DI gradualmente en código heredado
La tercera situación es especialmente realista.
Introducir Constructor Injection de una vez en todo el código existente supone una carga importante. En esos casos, usar Service Locator como puente temporal y migrar poco a poco a Constructor Injection me pareció más seguro.
Hoy se usan más contenedores de DI como Swinject o el @Environment de Swift que un Service Locator puro.
Internamente también obtienen objetos de un registro, pero la validación de registros y la gestión de ámbitos son más sólidas, lo que reduce el riesgo.
Preguntas frecuentes
P. ¿En qué se diferencia de Singleton?
Singleton convierte un objeto concreto en global, mientras que Service Locator actúa como un «almacén» para varios objetos. Su naturaleza es algo distinta.
P. ¿También se usa en SwiftUI?
@Environment y @EnvironmentObject de SwiftUI son conceptos bastante parecidos a Service Locator. En cierto modo, Apple ofrece un enfoque similar a nivel de framework.
P. Entonces, ¿qué debería usarse como opción predeterminada?
Recomiendo usar Constructor Injection como opción predeterminada. Usa Service Locator de forma local y solo donde sea necesario.
No existe una respuesta única para gestionar dependencias, pero la dirección está clara.
Haz que las dependencias sean visibles siempre que sea posible y usa con cuidado las herramientas que las ocultan, solo donde hagan falta. Con eso, el mantenimiento resulta mucho más sencillo.

