• X has a Y (for ex. as a property) • X is a Y (for ex. inherits from Y) • X depends on Z that depends on Y. How to remove dependency? • Create interface I (protocol) with Y’s methods and properties, used by X. • Y conforms to I. • X uses object of I type.
• X has a Y (for ex. as a property) • X is a Y (for ex. inherits from Y) • X depends on Z that depends on Y. How to remove dependency? • Create interface I (protocol) with Y’s methods and properties, used by X. • Y conforms to I. • X uses object of I type. Inversion of Control This is
protocol BiometricsManager { func validateUser() -> Result<Void, Error> } class LoginViewModel { private let loginService: LoginService private let biometricsManager: BiometricsManager init(loginService: LoginService, biometricsManager: BiometricsManager) { self.loginService = loginService self.biometricsManager = biometricsManager } } Constructor injection • LoginService and BiometricsManager - are Services. • LoginViewModel is Client. It’s easy to understand class dependencies Init method could have too many arguments
protocol BiometricsManager { func validateUser() -> Result<Void, Error> } class LoginViewModel { private var loginService: LoginService? private var biometricsManager: BiometricsManager? func setLoginService(_ loginService: LoginService) { self.loginService = loginService } func setBiometricsManager(_ biometricsManager: BiometricsManager) { self.biometricsManager = biometricsManager } } Setter injection • LoginService and BiometricsManager - are Services. • LoginViewModel is Client. It’s a good approach when you only have a few dependencies it’s easy to forget to set a necessary dependency
class LoginServiceImpl: LoginService { ... } protocol LoginServiceInjectable { var loginService: LoginService { get } } extension LoginServiceInjectable { var loginService: LoginService { LoginServiceInjector.loginService } } enum LoginServiceInjector { public private(set) static var loginService: LoginService = LoginServiceImpl() } // Same implementation of BiometricsManagerInjectable as LoginServiceInjectable class LoginViewModel: LoginServiceInjectable, BiometricsManagerInjectable { var error: Error? func login() { switch biometricsManager.validateUser() { case .success(): loginService.login() case .failure(let error): self.error = error } } } Interface injection • LoginServiceInjectable and BiometricsManagerInjectable - are Services. • LoginViewModel is Client. an injector can be completely unaware of the client’s actual implementation it’s di ff icult to see whole Client interface
• Mac OS X 10.10+ • watchOS 2.0+ • tvOS 9.0+ Installation • Cocoapods • Carthage • SPM Storyboard integration + SwiftUI Generic type registration problems (ObservableObject, View, etc.) + / - + Easy to understand and use - No Generics support - The dependency graph is created at runtime - Dependency is registered at runtime
• Mac OS X 10.10+ • watchOS 2.0+ • tvOS 9.0+ • iOS 8.3+ • macOS 10.10+ Installation • Cocoapods • Carthage • SPM • Cocoapods • Carthage • SPM Storyboard integration + - SwiftUI Generic type registration problems (ObservableObject, View, etc.) Components must have reference type only, Views could be Modules. + / - + Easy to understand and use - No Generics support - The dependency graph is created at runtime - Dependency is registered at runtime + generic types + powerful - A little bit complicated to start - The dependency graph is created at runtime - Dependency is registered at runtime
• Mac OS X 10.10+ • watchOS 2.0+ • tvOS 9.0+ • iOS 8.3+ • macOS 10.10+ • iOS 11.0+ • Mac OS X 10.14+ • tvOS 11.0+ Installation • Cocoapods • Carthage • SPM • Cocoapods • Carthage • SPM • Cocoapods • Carthage • SPM Storyboard integration + - + SwiftUI Generic type registration problems (ObservableObject, View, etc.) Components must have reference type only, Views could be Modules. Generic type registration problems (ObservableObject, View, etc.) + / - + Easy to understand and use - No Generics support - The dependency graph is created at runtime - Dependency is registered at runtime + generic types + powerful - A little bit complicated to start - The dependency graph is created at runtime - Dependency is registered at runtime + faster than Swinject + Easy to understand and use + property wrappers - property wrapper combination problems - The dependency graph is created at runtime - Dependency is registered at runtime
• Mac OS X 10.10+ • watchOS 2.0+ • tvOS 9.0+ • iOS 8.3+ • macOS 10.10+ • iOS 11.0+ • Mac OS X 10.14+ • tvOS 11.0+ • iOS 9.0+ • macOS 10.10 Installation • Cocoapods • Carthage • SPM • Cocoapods • Carthage • SPM • Cocoapods • Carthage • SPM • Cocoapods • Carthage • SPM • Homebrew (for Code Generator) Storyboard integration + - + - SwiftUI Generic type registration problems (ObservableObject, View, etc.) Components must have reference type only, Views could be Modules. Generic type registration problems (ObservableObject, View, etc.) Generic type registration problems (ObservableObject, View, etc.) + / - + Easy to understand and use - No Generics support - The dependency graph is created at runtime - Dependency is registered at runtime + generic types + powerful - A little bit complicated to start - The dependency graph is created at runtime - Dependency is registered at runtime + faster than Swinject + Easy to understand and use + property wrappers - property wrapper combination problems - The dependency graph is created at runtime - Dependency is registered at runtime + Compile-time safe + Displays missing dependencies - will be used along with Code Generator