Injeção de Dependência (Dependency Injection) em Swift: guia completo para iniciantes

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que seu código precisa de Dependency Injection?



Cada desenvolvedor iOS se depara com uma situação em que uma classe se torna "gorda" e rigidamente acoplada a implementações específicas. Você altera uma classe e precisa reescrever dezenas de outras. Familiar? Este é o problema clássico de alto acoplamento (tight coupling).



Dependency Injection (DI) é um padrão de projeto que resolve esse problema. Em vez de o objeto criar suas próprias dependências, elas são "injetadas" externamente. Isso torna o código flexível, testável e alinhado aos princípios SOLID (em particular, ao princípio da inversão de dependência).



Neste artigo, vamos explorar o que é DI em Swift, quais são as formas de implementá-lo e como ele simplifica a vida do desenvolvedor iOS/macOS. Usaremos exemplos simples, compreensíveis até para iniciantes.



O que é Dependency Injection? Um exemplo simples



Imagine que você está fazendo uma pizza. A versão sem DI: você mesmo cultiva o trigo, faz a farinha, ordenha a vaca para o queijo. Isso é alto acoplamento. A versão com DI: você simplesmente pede os ingredientes (dependências) ao fornecedor. Você não sabe como a farinha foi feita — só o resultado importa.



Na programação, é a mesma coisa. Suponha que temos uma classe OrderService que envia pedidos por e-mail:



// ❌ Sem DI — alto acoplamentoclass OrderService {    let mailer = Mailer() // Cria a dependência sozinho

func process(order: Order) { mailer.send(email: "Novo pedido: \\(order.id)") }}


Problema: não podemos substituir Mailer por uma versão de teste. A classe OrderService "sabe" demais.



// ✅ Com DI — a dependência é injetadaclass OrderService {    let mailer: MailerProtocol // Depende da abstração, não da implementação concreta

init(mailer: MailerProtocol) { // Injeção via inicializador self.mailer = mailer }

func process(order: Order) { mailer.send(email: "Novo pedido: \\(order.id)") }}


Agora podemos passar qualquer objeto que esteja em conformidade com o protocolo MailerProtocol — um serviço de e-mail real ou um "mock" para testes.



Três formas principais de injeção de dependência em Swift



Existem três formas populares de passar uma dependência para uma classe. Vamos analisar cada uma.



1. Constructor Injection (via inicializador)

A forma mais comum e preferida. A dependência é passada no momento da criação do objeto. Isso garante que o objeto esteja sempre em um estado válido.



protocol NetworkServiceProtocol {    func fetchData() -> String}

class RealNetworkService: NetworkServiceProtocol { func fetchData() -> String { return "Dados reais do servidor" }}

class MockNetworkService: NetworkServiceProtocol { func fetchData() -> String { return "Dados de teste" }}

class DataManager { private let network: NetworkServiceProtocol

// A dependência é passada via init init(network: NetworkServiceProtocol) { self.network = network }

func load() -> String { return network.fetchData() }}

// Usolet realManager = DataManager(network: RealNetworkService())let mockManager = DataManager(network: MockNetworkService())


Vantagens: Fica claro quais dependências são necessárias. O objeto está pronto para uso imediatamente. Desvantagens: Se houver muitas dependências, o inicializador pode se tornar extenso.



2. Property Injection (via propriedade)

A dependência é definida após a criação do objeto. Frequentemente usado quando a dependência é opcional ou difícil de obter no momento da inicialização.



class UserViewModel {    var apiService: NetworkServiceProtocol? // Dependência opcional

func loadUser() { apiServi

Blogs

Book Recommendations