Dependency Injection Framework for Swift, inspired by KOIN. Basically an implementation of service-locator pattern, living within the application's context.
Grow as you go!
We started small, it perfectly fits our use case.
DIKit can be installed using Carthage. After installing Carthage just add DIKit to your Cartfile:
github "Liftric/DIKit" ~> 1.6.1
CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. After installing CocoaPods add DIKit to your Podfile:
platform :ios, '9.0' pod 'DIKit', '~> 1.6.1'
- Define some sub
DependencyContainer(basically some sort of module declaration):
import DIKit public extension DependencyContainer { static var backend = module { single { Backend() as BackendProtocol } } } public extension DependencyContainer { static var network = module { single { Network() as NetworkProtocol } } } public extension DependenyContainer { static var app = module { single { AppState() as AppStateProtocol } factory { StopWatch() as StopWatchProtocol } } }
- Set the root
DependencyContainerand set it before the application gets initialised:
import DIKit @UIApplicationMain class AppDelegate: UIApplicationDelegate { override init() { super.init() DependencyContainer.defined(by: modules { .backend; .network; .app }) } }
Without sub DependencyContainer the following shorthand writing also does the job:
import DIKit @UIApplicationMain class AppDelegate: UIApplicationDelegate { override init() { super.init() DependencyContainer.defined(by: module { single { AppState() as AppStateProtocol } factory { StopWatch() as StopWatchProtocol } }) } }
- Inject the dependencies, for instance in a module:
import DIKit class Backend: BackendProtocol { @Inject var network: NetworkProtocol }
or a ViewController:
import DIKit class FirstViewController: UIViewController { // MARK: - Dependencies @LazyInject var backend: BackendProtocol @OptionalInject var stopwatch: StopWatchProtocol? // MARK: - View lifecycle override func viewWillAppear(_ animated: Bool) { let result = backend.fetch() print(result) } }
Injection via constructor:
import DIKit struct AppState: AppStateProtocol { private let backend: BackendProtocol init(backend: BackendProtocol = resolve()) { self.backend = backend } }
When registering your dependencies you can optionally define a tag. The tag can be anything, as long as it is AnyHashable.
This way you can register different resolvable dependencies for the same Type.
enum StorageContext: String { case userdata case systemdata } public extension DependencyContainer { static var app = module { factory(tag: StorageContext.systemdata) { LocalStorage() as LocalStorageProtocol } factory(tag: StorageContext.userdata) { LocalStorage() as LocalStorageProtocol } } }
You can then reference the same tag when resolving the type and can thus resolve different instances. Referencing the tag works with all injection methods.
import DIKit class Backend: BackendProtocol { @Inject(tag: StorageContext.systemdata) var injectedStorage: LocalStorageProtocol @LazyInject(tag: StorageContext.systemdata) var lazyInjectedStorage: LocalStorageProtocol @OptionalInject(tag: StorageContext.systemdata) var optionalInjectedStorage: LocalStorageProtocol? private let constructorInjectedStorage: LocalStorageProtocol init(storage: LocalStorageProtocol = resolve(tag: StorageContext.systemdata)) { self.constructorInjectedStorage = storage } }