À medida que um app iOS cresce, é comum a gestão de dependências virar uma grande dor de cabeça.
Mais cedo ou mais tarde, você encontra o padrão Swift Service Locator. Muitos acham conveniente, enquanto outros o rejeitam por considerá-lo um antipadrão.
Para mim, funcionou melhor usá-lo de forma seletiva, apenas nos pontos realmente necessários, em vez de adotá-lo por completo.
Indo direto à conclusão:
Service Locator não é uma alternativa completa ao DI (Dependency Injection, injeção de dependência); quando usado incorretamente, vira um antipadrão que esconde dependências. Ele se aproxima mais de uma ferramenta auxiliar.
Hoje vou explicar o que é esse padrão, por que ele é criticado e quando ainda pode ser útil, com base na minha experiência.
O que é o padrão Service Locator?
Em uma frase, é uma forma de obter os objetos necessários de um registro central.
Você mantém um único “registro” em algum lugar e procura os objetos ali sempre que precisa deles.
Se Constructor Injection fornece as dependências de fora para dentro, Service Locator as obtém diretamente de dentro.
Com o código, a ideia fica clara rapidamente.
// Estrutura que registra e obtém serviços de um 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 }
}
Faça o registro uma vez, na inicialização do app.
O código que usa esses serviços obtém as dependências necessárias assim.
// O view model “procura” diretamente suas dependências
final class FeedViewModel {
private let api: APIClient = ServiceLocator.shared.resolve()
// Funciona sem inserir api no construtor
}
Como você pode ver, a maior vantagem é deixar o construtor mais limpo.
Por que Service Locator é considerado um antipadrão?
O principal motivo é um só: as dependências ficam ocultas.
Vamos rever o código FeedViewModel acima. Só olhando o construtor, não dá para saber que essa classe usa APIClient.
Só é possível perceber isso ao abrir a implementação. Em um trabalho colaborativo, isso se torna bastante inconveniente.
O segundo problema são os testes.
Com Constructor Injection, basta passar um Mock durante o teste. Já com Service Locator, é preciso alterar o estado do registro global, então os testes podem interferir facilmente uns nos outros.
O terceiro é o risco em tempo de execução.
Ao tentar obter uma dependência não registrada, o app trava durante a execução, e não na compilação. O cast forçado as! do exemplo acima é exatamente o ponto problemático.
Resumindo:
| Item de comparação | Constructor Injection (DI) | Service Locator |
|---|---|---|
| Exposição das dependências | Claramente visível | Oculta internamente |
| Facilidade de teste | Alta | Relativamente baixa |
| Momento da descoberta de erros | Compilação | Execução |
| Concisão do construtor | Mais argumentos | Limpo |
Então, quando não há problema em usá-lo?
Ele não é necessariamente ruim. Achei útil em situações como estas.
- Um único serviço compartilhado em todo o app, como um logger ou uma ferramenta de analytics
- Quando o caminho de Constructor Injection é profundo demais e os argumentos continuam sendo repassados
- Um período de transição para introduzir DI gradualmente em código legado
O terceiro caso é especialmente realista.
Adicionar Constructor Injection de uma vez a todo o código existente é pesado. Nesses casos, usar Service Locator como uma ponte temporária e migrar aos poucos para Constructor Injection pareceu mais seguro.
Hoje, contêineres de DI como Swinject ou o @Environment do Swift são mais usados que um Service Locator puro.
Eles também obtêm objetos de um registro internamente, mas oferecem validação de registros e gerenciamento de escopo mais robustos, reduzindo os riscos.
Perguntas frequentes
P. Qual é a diferença em relação a Singleton?
Singleton torna um objeto específico global, enquanto Service Locator funciona como um “depósito” para vários objetos. A natureza dos dois é um pouco diferente.
P. Também é usado no SwiftUI?
O @Environment e o @EnvironmentObject do SwiftUI são conceitos bastante parecidos com Service Locator. Na prática, a Apple oferece uma abordagem semelhante no nível do framework.
P. Afinal, o que deve ser usado como padrão?
Recomendo usar Constructor Injection por padrão. Use Service Locator localmente, apenas onde for realmente necessário.
Não existe uma resposta única para a gestão de dependências, mas a direção é clara.
Deixe as dependências visíveis sempre que possível e use com cuidado as ferramentas que as ocultam, apenas onde forem necessárias. Só isso já torna a manutenção muito mais fácil.

