Swinject — Container de Injeção de Dependência para Swift (iOS/macOS)
Injeção de Dependência (DI) é um dos princípios chave do SOLID, permitindo criar código fracamente acoplado, testável e escalável. No mundo Swift para desenvolvimento iOS e macOS, uma das bibliotecas mais populares que implementam DI é o Swinject. É um container de injeção de dependência leve, mas poderoso, que simplifica o gerenciamento de objetos e seu ciclo de vida.
Neste artigo, vamos detalhar como o Swinject funciona, como instalá-lo e usá-lo em projetos reais.
O que é Injeção de Dependência e para que serve um container?
Injeção de dependência é uma técnica na qual um objeto recebe suas dependências externamente, em vez de criá-las por si só. Isso aumenta a flexibilidade e simplifica os testes. Um container DI (por exemplo, Swinject) automatiza o processo de criação e vinculação de objetos, poupando o desenvolvedor de escrever código boilerplate.
Instalação do Swinject
O Swinject pode ser instalado usando Swift Package Manager, CocoaPods ou Carthage. Vamos considerar o método mais moderno — SPM:
// No Xcode: File → Swift Packages → Add Package Dependency// URL: https://github.com/Swinject/Swinject.git// Versão: a partir de 2.8.0Ou adicione no Package.swift:
dependencies: [ .package(url: "https://github.com/Swinject/Swinject.git", from: "2.8.0")]Componentes principais do Swinject
- Container — o objeto principal onde as dependências são registradas e resolvidas.
- Service — o tipo (protocolo ou classe) que queremos injetar.
- Registration — o processo de vincular uma abstração (protocolo) a uma implementação concreta.
- Scope — gerencia o ciclo de vida do objeto (transiente, singleton, etc.).
Exemplo básico de uso
Suponha que temos um protocolo NetworkServiceProtocol e sua implementação NetworkService:
import Swinject
protocol NetworkServiceProtocol { func fetchData() -> String}
class NetworkService: NetworkServiceProtocol { func fetchData() -> String { return "Dados obtidos" }}
// Criamos o containerlet container = Container()
// Registramos a dependênciacontainer.register(NetworkServiceProtocol.self) { _ in NetworkService() }
// Resolvemos (obtemos) a instâncialet service = container.resolve(NetworkServiceProtocol.self)!print(service.fetchData()) // "Dados obtidos"Injeção de dependências no ViewController (iOS)
Na maioria das vezes, o Swinject é usado para injetar serviços nos controladores. Vamos ver um exemplo com uma view-model:
import Swinject
// Protocolo e implementação da view-modelprotocol UserViewModelProtocol { func getUsers() -> [String]}
class UserViewModel: UserViewModelProtocol { private let networkService: NetworkServiceProtocol init(networkService: NetworkServiceProtocol) { self.networkService = networkService } func getUsers() -> [String] { // Usamos networkService para carregar return ["Alice", "Bob"] }}
// Registro no containercontainer.register(UserViewModelProtocol.self) { resolver in let network = resolver.resolve(NetworkServiceProtocol.self)! return UserViewModel(networkService: network)}
// Uso no controladorclass UsersViewController: UIViewController { var viewModel: UserViewModelProtocol? override func viewDidLoad() { super.viewDidLoad() viewModel = container.resolve(UserViewModelProtocol.self) print(viewModel?.getUsers() ?? []) }}Ciclos de vida dos objetos (Scopes)
O Swinject suporta vários scopes:
- .transient