Injeção de Dependência em Swift: princípios e exemplos para iOS/macOS

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
```html

Introdução à Dependency Injection em Swift



Dependency Injection (DI) é um dos princípios-chave de design que ajuda a criar aplicações flexíveis, testáveis e de fácil manutenção em Swift. No contexto do desenvolvimento iOS e macOS, a DI permite separar a criação de objetos do seu uso, o que simplifica significativamente os testes e a modificação do código.



A ideia principal é que uma classe não deve criar suas próprias dependências. Em vez disso, as dependências são "injetadas" externamente. Esta é uma implementação do Princípio da Inversão de Dependência (Dependency Inversion Principle) do SOLID.



Por que a Dependency Injection é importante para o desenvolvimento iOS/macOS?



Em aplicações nativas em Swift, o problema de acoplamento rígido (tight coupling) é comum. Vejamos um exemplo típico:



class UserService {    let networkManager = NetworkManager()    let database = Database()        func fetchUser(id: Int) -> User {        // lógica    }}


Aqui, UserService está rigidamente acoplado às implementações específicas de NetworkManager e Database. Isso cria vários problemas:



  • Dificuldade de teste — é impossível substituir os serviços reais por mocks ou stubs
  • Baixa reutilização — a classe não pode ser usada com outras implementações
  • Violação do SRP — a classe não lida apenas com a lógica de negócios, mas também com a criação de dependências


A Dependency Injection resolve esses problemas, permitindo injetar dependências através do construtor, propriedades ou métodos.



Formas de implementar Dependency Injection em Swift



1. Injeção via construtor (Constructor Injection)



A forma mais comum e recomendada. As dependências são passadas através do inicializador:



protocol NetworkService {    func request(endpoint: String) -> Data}

protocol DatabaseService { func save(user: User)}

class UserService { private let networkService: NetworkService private let databaseService: DatabaseService init(networkService: NetworkService, databaseService: DatabaseService) { self.networkService = networkService self.databaseService = databaseService } func fetchUser(id: Int) -> User? { let data = networkService.request(endpoint: "users/\\(id)") // parsing dos dados return User(id: id, name: "John") }}


Vantagens: as dependências são explicitamente visíveis, obrigatórias para o funcionamento da classe e fáceis de testar.



2. Injeção via propriedade (Property Injection)



As dependências são definidas após a criação do objeto:



class AuthManager {    var networkService: NetworkService?        func login(username: String, password: String) -> Bool {        guard let service = networkService else { return false }        // lógica de autenticação        return true    }}

// Usolet authManager = AuthManager()authManager.networkService = RealNetworkService()


Desvantagem: as dependências podem não ser definidas, o que leva a tipos opcionais e potenciais crashes.



3. Injeção via método (Method Injection)



A dependência é passada diretamente no método:



class DataProcessor {    func process(data: Data, using service: NetworkService) -> ProcessedData {        let result = service.request(endpoint: "process")        // processamento        return ProcessedData()    }}


Adequado para casos onde a dependência é necessária apenas para um método específico.



Exemplo prático: aplicativo de gerenciamento de tarefas



Vamos criar uma aplicação simples para iOS usando DI:



// Protocolos de dependênciasprotocol TaskRepository {    func fetchTasks() ->
```                

Blogs

Book Recommendations