Skip to content

Instantly share code, notes, and snippets.

@nonameplum
Last active July 11, 2019 19:08
Show Gist options
  • Save nonameplum/676e475ce7dbb83fee0bce783557036e to your computer and use it in GitHub Desktop.
Save nonameplum/676e475ce7dbb83fee0bce783557036e to your computer and use it in GitHub Desktop.
Very simple dependency container for swift
import Foundation
public class Container {
//MARK:- Types
public enum ObjectLifeTime {
case persistent
case transient
}
private class Resolver {
let factory: (Container) -> Any
let objectLifeTime: ObjectLifeTime
var persistentObject: Any? = nil
init(factory: @escaping (Container) -> Any, objectLifeTime: ObjectLifeTime) {
self.factory = factory
self.objectLifeTime = objectLifeTime
}
func resolve(in container: Container) -> Any {
if objectLifeTime == .persistent {
if persistentObject != nil {
return persistentObject!
}
persistentObject = factory(container)
return persistentObject!
}
return factory(container)
}
}
//MARK:- Properties
private var dependencies: [String: Resolver] = [:]
//MARK:- Convenience
private func keyForType<T>(_ type: T.Type, name: String?) -> String {
return name ?? String(describing: type)
}
//MARK:- Public methods
public func register<T>(type: T.Type, name: String? = nil, objectLifeTime: ObjectLifeTime = .persistent, _ factory: @escaping (Container) -> Any) {
dependencies[keyForType(type, name: name)] = Resolver(factory: factory, objectLifeTime: objectLifeTime)
}
public func resolve<T>(type: T.Type, name: String? = nil) -> T {
guard let resolver = dependencies[keyForType(type, name: name)] else {
fatalError("Couldn't find definition for type: \(String(describing: type))" + (name != nil ? " registered with custom name: \(name!)" : ""))
}
let dependency = resolver.resolve(in: self) as! T
return dependency
}
deinit {
print("💥 Deinited \(String(describing: type(of: self)))")
}
}
///////////////////////////////////////////////////
protocol ServiceProtocol {}
protocol InteractorProtocol: class {
var service: ServiceProtocol { get }
}
class Service: ServiceProtocol {}
class Interactor: InteractorProtocol {
let service: ServiceProtocol
init(service: ServiceProtocol) {
self.service = service
}
}
///////////////////////////////////////////////////
// Check persistent case
var _container: Container? = Container()
_container?.register(type: ServiceProtocol.self, { (container) in
return Service()
})
_container!.register(type: InteractorProtocol.self) { (container) in
let service = container.resolve(type: ServiceProtocol.self)
return Interactor(service: service)
}
let interactor1 = _container!.resolve(type: InteractorProtocol.self)
print(interactor1) // Dependency resolved!
let interactor2 = _container!.resolve(type: InteractorProtocol.self)
print(interactor2)
print("should be equal: \(interactor1 === interactor2)")
_container = nil // Deinit should be called!
///////////////////////////////////////////////////
// Check transient case
var _container2: Container? = Container()
_container2?.register(type: ServiceProtocol.self, { (container) in
return Service()
})
_container2!.register(type: InteractorProtocol.self, objectLifeTime: .transient) { (container) in
let service = container.resolve(type: ServiceProtocol.self)
return Interactor(service: service)
}
let interactor11 = _container2!.resolve(type: InteractorProtocol.self)
print(interactor11) // Dependency resolved!
let interactor22 = _container2!.resolve(type: InteractorProtocol.self)
print(interactor22)
print("shouldn't be equal: \(interactor11 === interactor22)")
_container2 = nil // Deinit should be called!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment