Design de software

Quando adotar um contêiner de DI? Resumo sobre Swinject e Factory

Ao desenvolver para iOS, chega um momento em que esta pergunta aparece.

4 min de leitura
Imagem de capa de Quando adotar um contêiner de DI? Resumo sobre Swinject e Factory

Ao desenvolver para iOS, chega um momento em que esta pergunta aparece.

“Se posso passar isso pelo construtor, preciso mesmo usar um contêiner de DI?”

Quando um projeto pessoal cresce, todo mundo enfrenta essa dúvida alguma vez. Testei Swinject e Factory em projetos reais e estabeleci um critério.

Vou direto à conclusão.

Com menos de 20 telas, a injeção pelo construtor é suficiente, sem contêiner de DI.

A hora de adotar chega quando o grafo de dependências fica complexo e a troca de mocks nos testes se torna frequente.

E, se for adotar um, escolha Factory.

Vou explicar quando adotar e por que escolher Factory, na ordem em que vivi isso.

Antes de adotar um contêiner de DI, verifique isto

Vamos começar esclarecendo o que costuma causar confusão.

Injeção de dependência (DI) e contêiner de DI são coisas diferentes.

Injeção de dependência é o hábito de projeto de fornecer os objetos necessários de fora. Ela não exige nenhuma biblioteca.

// Isto também já é uma excelente injeção de dependência
final class LoginViewModel {
    private let authService: AuthService
    init(authService: AuthService) {
        self.authService = authService
    }
}

Já o contêiner de DI é uma ferramenta que gerencia, em um único lugar, onde e como criar esses objetos.

Se a injeção pelo construtor é suficiente, ainda é cedo para um contêiner.

Estes são os sinais que uso para decidir pela adoção.

  1. Quando o código de inicialização se repete em cada tela e começa a ficar difícil passar objetos
  2. Quando você troca objetos reais por objetos falsos com frequência nos testes
  3. Quando várias partes do app precisam compartilhar a mesma instância (com perfil de singleton)
  4. Quando a equipe cresce e passa a perguntar com frequência “onde este objeto é criado?”

Se dois ou mais itens se aplicarem, começo a considerar um contêiner.


Qual é a diferença entre Swinject e Factory?

Swinject é o contêiner de runtime tradicional do ecossistema Swift, usado desde 2015. Você registra em Container e obtém por resolve. O problema é que resolve retorna um opcional. Se o registro for esquecido, a compilação passa normalmente e o app só quebra ao forçar o desempacotamento de nil naquela tela.

Factory surgiu para resolver justamente as limitações desses contêineres tradicionais. Michael Long, criador do Resolver, o redesenhou com base nessa experiência. A ideia central é: “a definição é o registro”. Como as factories são definidas como propriedades do contêiner, não existe o conceito de esquecer um registro. Uma referência inválida gera erro em tempo de compilação, não em runtime.

Esta tabela resume as diferenças que percebi até 2026.

Item Swinject Factory
Forma de registrar e resolver resolve em runtime (retorna opcional) Definição baseada em tipo + property wrapper
Quando falta um registro nil/falha em runtime Bloqueio antecipado por erro de compilação
Gerenciamento de escopo Compatível Compatível (.singleton, .cached, .shared etc.)
Dependências externas Dependência de framework separado Pacote único e leve
Curva de aprendizado Um pouco acentuada Relativamente suave

Antes, o conselho era “use Swinject se precisar de gerenciamento complexo de escopo”, mas isso mudou. Factory também oferece suporte completo a singleton, cache e escopo compartilhado. Factory faz com mais segurança o que Swinject fazia.

Ao testar na prática, descobri que Factory começa com um único arquivo
Ao testar na prática, descobri que Factory começa com um único arquivo

Por isso, a conclusão é Factory

Estes foram os critérios que usei na prática.

Se for adotar agora, Factory sozinho basta

Em projetos individuais ou de equipe, Factory foi simples de adotar porque permite começar com um pacote e sem dependências externas. O decisivo foi eliminar estruturalmente os incidentes de runtime causados por esquecer um registro.

// Factory: A definição é o registro
extension Container {
    var authService: Factory<AuthService> {
        self { LiveAuthService() }.singleton  // O escopo também cabe em uma linha
    }
}
// Lado consumidor
@Injected(\.authService) private var authService

Trocar por um mock nos testes também exige apenas uma linha.

Container.shared.authService.register { MockAuthService() }

Quando manter o Swinject?

Ao manter uma base de código grande construída com Swinject. Não há motivo para remover agora uma estrutura de assembly que funciona bem. Ainda assim, mais equipes estão migrando gradualmente para Factory, começando pelos módulos novos. Em uma adoção nova, ficou difícil justificar a escolha do Swinject.


Perguntas frequentes (Q&A)

P. Não posso começar com um contêiner desde o início?

Não vou impedir, mas não recomendo. Quando o grafo de dependências é simples, o contêiner apenas esconde mais uma camada do código e dificulta rastreá-lo.

P. Posso migrar de Swinject para Factory?

Sim. Manter os dois contêineres por um período e migrar por módulo é o caminho mais realista. Como é preciso revisar os pontos de registro e as configurações de escopo, projetos grandes levam tempo, mas depois a preocupação com crashes em runtime desaparece.

P. Qual é melhor para um projeto SwiftUI?

Sem dúvida, Factory. Por ser baseado em property wrappers, combina naturalmente com o estilo declarativo do SwiftUI, e inserir um mock no preview é simples.


O mais limpo é adotar um contêiner de DI quando ele se torna necessário.

Se decidir adotar um, escolha Factory sem hesitar. Ele nasceu para corrigir as limitações dos contêineres existentes e funciona tanto para começar pequeno quanto para crescer. Conte quantos dos quatro sinais anteriores se aplicam ao seu projeto hoje.