Skip to content

Repository files navigation

SwiftInjection

Build StatusVersion

A dependency container for Swift

Setting up Dependencies

A Module file is where you define your dependencies. The goal is to abstract out all your dependencies in this file. The only class in your project that should know about concrete implementations should be the module class, the rest of the classes in your application should be using these implementations through interfaces.

You could have multiple module classes in order to organize your dependencies

publicclassAppModule:DIModule{publicfunc load(container:DIContainer){
container.bind(){UserDefaults.standard()}
container.bind(){URLSession.shared()}
container.bind(type:HttpService.self){HttpClient(baseUrl:"https://api.github.com", urlSession: $0))}
container.bind(type:GithubService.self){GithubHttpClient(httpService: $0)}
container.bind(type:AnalyticsTracker.self, named:GoogleAnalyticsTracker.analyticsIdentifier()){GoogleAnalyticsTracker()}
container.bind(type:AnalyticsTracker.self, named:AmplitudeAnalyticsTracker.analyticsIdentifier()){AmplitudeAnalyticsTracker()}}}classAppDelegate:UIResponder,UIApplicationDelegate{overrideinit(){
super.init()DIContainer.instance.addModule(AppModule())}}

Binding Internal classes

avoid direct use of singletons to make your code more testable

container.bind(){URLSession.shared()}

Binding classes as singleton

Instead of adding singleton logic to your classes simply bind them as singleton Note: Structs are not compatible with singleton pattern

// Bind class as singleton
bind(asSingleton:true){Session()}
// Bind protocol to an implementation as singleton
bind(AnalyticsTracker.self, asSingleton:true){GoogleAnalyticsTracker()}

Bind Named Instances

In cases where you have multiple implementations for a single protocol you can use named binding to retrieve the correct instance

bind(AnalyticsTracker.self, named:"Google"){GoogleAnalyticsTraker()}bind(AnalyticsTracker.self, named:"Amplitude"){AmplitudeAnalyticsTracker()}
// Inject expected instance
letgoogleAnalyticsTracker:AnalyticsTracker=inject(named:"Google")letamplitudeAnalyticsTracker:AnalyticsTracker=inject(named:"Amplitude")
// Get all implementations for a given protocol (great for chain of responssibilities)
lettrackers:[AnalyticsTracker]=injectAll()

Property Injection

Only use property injection on root level, for anything else below the viewController use constructor injection

classViewController:UIViewController{letgithubService:GithubService=inject() // Injects the implementation defined in module
letsession=inject(Session.self) // injects the singleton instance
letanalyticTrackers:[AnalyticsTracker]=injectAll() // Injects all implemetations of AnalyticsTracker
}

Constructor Injection

Simpy pass dependencies through the intiializer and define binding in the module file

protocolGithubService{}protocolHttpService{}classGithubHttpClient:GithubService{lethttpService:HttpService
// Constructor injection
init(httpService:HttpService){self.httpService = httpService
}}classAppModule:DIModule{func load(container:DIContainer){
container.bind(type:URLSession.self){URLSession.shared()}
container.bind(type:HttpService.self){HttpClient(baseUrl:"https://api.github.com", urlSession: $0))}
container.bind(type:GithubService.self){GithubHttpClient(httpService: $0)}}}classViewController:UIViewController{
// Property Injection
// This will return an instance of GithubHttpClient with all depndencies as defined in module
letgithubService:GithubService=inject()}

About

Dependency Injection framework for Swift

Topics

Resources

Stars

22 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages