Last active
March 3, 2023 22:24
-
-
Save dcarmo-tribalscale/cf0713921b7f79f2f26bedc92d45de0d to your computer and use it in GitHub Desktop.
Dependency Injection Blog
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 1 | |
| public protocol DIContainer { | |
| func register<DependencyType>(type: DependencyType.Type, dependency: DependencyType) | |
| func resolve<DependencyType>(type: DependencyType.Type) -> DependencyType | |
| } | |
| // 2 | |
| public class DependencyContainer { | |
| public static let shared: DIContainer = DependencyContainer() | |
| private var registeredDependencies: [String: Any] = [:] | |
| } | |
| // 3 | |
| extension DependencyContainer: DIContainer { | |
| public func register<DependencyType>(type: DependencyType.Type, dependency: DependencyType) { | |
| let key = dependencyKey(for: type) | |
| registeredDependencies[key] = dependency | |
| } | |
| public func resolve<DependencyType>(type: DependencyType.Type) -> DependencyType { | |
| let key = dependencyKey(for: type) | |
| guard let dependency = registeredDependencies[key] as? DependencyType else { | |
| preconditionFailure("DependencyContainer.resolve. There is no dependency registered for this type. Please register a dependency for this type.") | |
| } | |
| return dependency | |
| } | |
| private func dependencyKey<DependencyType>(for type: DependencyType.Type) -> String { | |
| String(describing: type) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 1 | |
| DependencyContainer.shared.register(type: NetworkingLayer.self, dependency: AppNetworkingLayer()) | |
| class ViewController: UIViewController { | |
| let networkingLayer: NetworkingLayer | |
| // 2 | |
| init(networkingLayer: NetworkingLayer = DependencyContainer.shared.resolve(type: NetworkingLayer.self)) { | |
| self.networkingLayer = networkingLayer | |
| super.init(nibName: nil, bundle: nil) | |
| } | |
| required init?(coder: NSCoder) { | |
| fatalError("init(coder:) has not been implemented") | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public protocol DIContainer { | |
| func register<DependencyType>(type: DependencyType.Type, dependency: DependencyType) | |
| // 1 | |
| func register<DependencyType>(key: String, dependency: DependencyType) | |
| func resolve<DependencyType>(type: DependencyType.Type) -> DependencyType | |
| // 2 | |
| func resolve<DependencyType>(key: String) -> DependencyType | |
| } | |
| public class DependencyContainer { | |
| public static let shared: DIContainer = DependencyContainer() | |
| private var registeredDependencies: [String: Any] = [:] | |
| } | |
| extension DependencyContainer: DIContainer { | |
| public func register<DependencyType>(type: DependencyType.Type, dependency: DependencyType) { | |
| register(key: dependencyKey(for: type), dependency: dependency) | |
| } | |
| // 3 | |
| public func register<DependencyType>(key: String, dependency: DependencyType) { | |
| registeredDependencies[key] = dependency | |
| } | |
| public func resolve<DependencyType>(type: DependencyType.Type) -> DependencyType { | |
| return resolve(key: dependencyKey(for: type)) | |
| } | |
| // 4 | |
| public func resolve<DependencyType>(key: String) -> DependencyType { | |
| guard let dependency = registeredDependencies[key] as? DependencyType else { | |
| preconditionFailure("DependencyContainer.resolve. There is no dependency registered for this type. Please register a dependency for this type.") | |
| } | |
| return dependency | |
| } | |
| private func dependencyKey<DependencyType>(for type: DependencyType.Type) -> String { | |
| String(describing: type) | |
| } | |
| } | |
| @propertyWrapper | |
| public struct Inject<Value> { | |
| private(set) public var wrappedValue: Value | |
| // 5 | |
| public init(key: String? = nil) { | |
| // 6 | |
| if let key = key { | |
| self.wrappedValue = DependencyContainer.shared.resolve(key: key) | |
| } else { | |
| self.wrappedValue = DependencyContainer.shared.resolve(type: Value.self) | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| DependencyContainer.shared.register(type: NetworkingLayer.self, dependency: AppNetworkingLayer()) | |
| // 1 | |
| DependencyContainer.shared.register(key: "MockedNetworkingLayer", dependency: MockNetworkingLayer()) | |
| class ViewControllerInjected: UIViewController { | |
| // 2 | |
| @Inject | |
| var networkingLayer: NetworkingLayer | |
| // 3 | |
| @Inject(key: "MockedNetworkingLayer") | |
| var mockedNetworkingLayer: NetworkingLayer | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public protocol DIContainer { | |
| // 1 | |
| func register<DependencyType>(type: DependencyType.Type, dependency: @escaping () -> DependencyType) | |
| func register<DependencyType>(key: String, dependency: @escaping () -> DependencyType) | |
| func resolve<DependencyType>(type: DependencyType.Type) -> DependencyType | |
| func resolve<DependencyType>(key: String) -> DependencyType | |
| } | |
| public class DependencyContainer { | |
| public static let shared: DIContainer = DependencyContainer() | |
| // 2 | |
| private var dependencyInitializer: [String: () -> Any] = [:] | |
| } | |
| extension DependencyContainer: DIContainer { | |
| // 3 | |
| public func register<DependencyType>(type: DependencyType.Type, dependency: @escaping () -> DependencyType) { | |
| register(key: dependencyKey(for: type), dependency: dependency) | |
| } | |
| public func register<DependencyType>(key: String, dependency: @escaping () -> DependencyType) { | |
| dependencyInitializer[key] = dependency | |
| } | |
| public func resolve<DependencyType>(type: DependencyType.Type) -> DependencyType { | |
| return resolve(key: dependencyKey(for: type)) | |
| } | |
| public func resolve<DependencyType>(key: String) -> DependencyType { | |
| // 4 | |
| guard let dependency = dependencyInitializer[key]?() as? DependencyType else { | |
| preconditionFailure("DependencyContainer.resolve. There is no dependency registered for this type. Please register a dependency for this type.") | |
| } | |
| return dependency | |
| } | |
| private func dependencyKey<DependencyType>(for type: DependencyType.Type) -> String { | |
| String(describing: type) | |
| } | |
| } | |
| @propertyWrapper | |
| public struct Inject<Value> { | |
| private(set) public var wrappedValue: Value | |
| public init(key: String? = nil) { | |
| if let key = self.key { | |
| self.wrappedValue = DependencyContainer.shared.resolve(key: key) | |
| } else { | |
| self.wrappedValue = DependencyContainer.shared.resolve(type: Value.self) | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 1 | |
| DependencyContainer.shared.register(type: NetworkingLayer.self, dependency: { | |
| AppNetworkingLayer() | |
| }) | |
| DependencyContainer.shared.register(key: "MockedNetworkingLayer", dependency: { | |
| MockNetworkingLayer() | |
| }) | |
| DependencyContainer.shared.register(type: User.self, dependency: { | |
| let cache = UserCache() | |
| return cache.user | |
| }) | |
| class ViewControllerInjected: UIViewController { | |
| @Inject | |
| var networkingLayer: NetworkingLayer | |
| @Inject(key: "MockedNetworkingLayer") | |
| var mockedNetworkingLayer: NetworkingLayer | |
| // 2 | |
| @Inject | |
| var user: User | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 1 | |
| public enum ResolveMode { | |
| case new | |
| case shared | |
| } | |
| public protocol DIContainer { | |
| func register<DependencyType>(type: DependencyType.Type, dependency: @escaping () -> DependencyType) | |
| func register<DependencyType>(key: String, dependency: @escaping () -> DependencyType) | |
| // 2 | |
| func resolve<DependencyType>(type: DependencyType.Type, mode: ResolveMode) -> DependencyType | |
| func resolve<DependencyType>(key: String, mode: ResolveMode) -> DependencyType | |
| } | |
| public class DependencyContainer { | |
| public static let shared: DIContainer = DependencyContainer() | |
| // 3 | |
| private var dependencyInitializer: [String: () -> Any] = [:] | |
| // 4 | |
| private var dependencyShared: [String: Any] = [:] | |
| } | |
| extension DependencyContainer: DIContainer { | |
| public func register<DependencyType>(type: DependencyType.Type, dependency: @escaping () -> DependencyType) { | |
| register(key: dependencyKey(for: type), dependency: dependency) | |
| } | |
| public func register<DependencyType>(key: String, dependency: @escaping () -> DependencyType) { | |
| dependencyInitializer[key] = dependency | |
| } | |
| public func resolve<DependencyType>(type: DependencyType.Type, mode: ResolveMode) -> DependencyType { | |
| return resolve(key: dependencyKey(for: type), mode: mode) | |
| } | |
| public func resolve<DependencyType>(key: String, mode: ResolveMode) -> DependencyType { | |
| // 5 | |
| switch mode { | |
| case .new: | |
| // 6 | |
| guard let newDependency = dependencyInitializer[key]?() as? DependencyType else { | |
| preconditionFailure("DependencyContainer.resolve. There is no dependency registered for this type. Please register a dependency for this type.") | |
| } | |
| return newDependency | |
| case .shared: | |
| // 7 | |
| if dependencyShared[key] == nil, let dependency = dependencyInitializer[key]?() { | |
| dependencyShared[key] = dependency | |
| } | |
| // 8 | |
| guard let sharedDependency = dependencyShared[key] as? DependencyType else { | |
| preconditionFailure("DependencyContainer.resolve. There is no dependency registered for this type. Please register a dependency for this type.") | |
| } | |
| return sharedDependency | |
| } | |
| } | |
| private func dependencyKey<DependencyType>(for type: DependencyType.Type) -> String { | |
| String(describing: type) | |
| } | |
| } | |
| @propertyWrapper | |
| public struct Inject<Value> { | |
| private(set) public var wrappedValue: Value | |
| // 9 | |
| public init(key: String? = nil, mode: ResolveMode = .shared) { | |
| // 10 | |
| if let key = key { | |
| wrappedValue = DependencyContainer.shared.resolve(key: key, mode: mode) | |
| } else { | |
| wrappedValue = DependencyContainer.shared.resolve(type: Value.self, mode: mode) | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| DependencyContainer.shared.register(type: NetworkingLayer.self, dependency: { | |
| AppNetworkingLayer() | |
| }) | |
| DependencyContainer.shared.register(key: "MockedNetworkingLayer", dependency: { | |
| MockNetworkingLayer() | |
| }) | |
| DependencyContainer.shared.register(type: User.self, dependency: { | |
| let cache = UserCache() | |
| return cache.user | |
| }) | |
| class ViewControllerInjected: UIViewController { | |
| @Inject | |
| var networkingLayer: NetworkingLayer | |
| @Inject(key: "MockedNetworkingLayer") | |
| var mockedNetworkingLayer: NetworkingLayer | |
| // 1 | |
| @Inject(mode: .new) | |
| var user: User | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 1 | |
| @propertyWrapper | |
| // 2 | |
| public struct Inject<Value> { | |
| private(set) public var wrappedValue: Value | |
| public init() { | |
| // 3 | |
| self.wrappedValue = DependencyContainer.shared.resolve(type: Value.self) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class ViewControllerInjected: UIViewController { | |
| // 1 | |
| @Inject | |
| var networkingLayer: NetworkingLayer | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @propertyWrapper | |
| public struct Inject<Value> { | |
| private(set) public var wrappedValue: Value | |
| // 1 | |
| public init(container: DIContainer = DependencyContainer.shared, key: String? = nil, mode: ResolveMode = .shared) { | |
| if let key = key { | |
| wrappedValue = container.resolve(key: key, mode: mode) | |
| } else { | |
| wrappedValue = container.resolve(type: Value.self, mode: mode) | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| let networkContainer: DIContainer = DependencyContainer() | |
| class ViewControllerInjected: UIViewController { | |
| @Inject | |
| var networkingLayer: NetworkingLayer | |
| @Inject(key: "MockedNetworkingLayer") | |
| var mockedNetworkingLayer: NetworkingLayer | |
| @Inject | |
| var user: User | |
| // 1 | |
| @Inject(container: networkContainer) | |
| var fromNetworkContainer: NetworkingLayer | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @propertyWrapper | |
| public struct LazyInject<Value> { | |
| // 1 | |
| private let container: DIContainer | |
| private let key: String? | |
| private let mode: ResolveMode | |
| // 2 | |
| private(set) public lazy var wrappedValue: Value = { | |
| if let key = self.key { | |
| return self.container.resolve(key: key, mode: self.mode) | |
| } else { | |
| return self.container.resolve(type: Value.self, mode: self.mode) | |
| } | |
| }() | |
| // 3 | |
| public init(container: DIContainer = DependencyContainer.shared, key: String? = nil, mode: ResolveMode = .shared) { | |
| self.container = container | |
| self.key = key | |
| self.mode = mode | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 1 | |
| DependencyContainer.shared.register(type: NetworkingLayer.self, dependency: { | |
| AppNetworkingLayer() | |
| }) | |
| DependencyContainer.shared.register(type: UserCache.self, dependency: { | |
| UserCache() | |
| }) | |
| class UserCache { | |
| // 2 | |
| @LazyInject | |
| var network: NetworkingLayer | |
| let user: User = User(firstName: "First", | |
| lastName: "Last", | |
| email: "first.last@gmail.com", | |
| isPushEnabled: true, | |
| isLocationServicesEnabled: false) | |
| } | |
| protocol NetworkingLayer: AnyObject { | |
| func makeRequest() | |
| } | |
| class AppNetworkingLayer: NetworkingLayer { | |
| // 3 | |
| @LazyInject | |
| var user: UserCache | |
| func makeRequest() { | |
| print("AppNetworkingLayer made a request") | |
| } | |
| } | |
| class ViewControllerInjected: UIViewController { | |
| // 4 | |
| @Inject | |
| var networkingLayer: NetworkingLayer | |
| @Inject | |
| var user: User | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| protocol NetworkingLayer { | |
| func makeRequest() | |
| } | |
| class AppNetworkingLayer: NetworkingLayer { | |
| func makeRequest() { | |
| print("AppNetworkingLayer made a request") | |
| } | |
| } | |
| class MockNetworkingLayer: NetworkingLayer { | |
| func makeRequest() { | |
| print("MockNetworkingLayer made a request") | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import XCTest | |
| @testable import YourProject | |
| class ClassToTest: XCTestCase { | |
| override func setUp() { | |
| super.setUp() | |
| // Put setup code here. This method is called before the invocation of each test method in the class. | |
| // 1 | |
| DependencyContainer.shared.register(type: NetworkingLayer.self, dependency: { | |
| MockNetworkingLayer() | |
| }) | |
| } | |
| // Tests here | |
| override func tearDown() { | |
| // 2 | |
| DependencyContainer.shared.remove(type: NetworkingLayer.self) | |
| // Put teardown code here. This method is called after the invocation of each test method in the class. | |
| super.tearDown() | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @propertyWrapper | |
| // 1 | |
| public struct WeakInject<Value> { | |
| // 2 | |
| private weak var underlyingValue: AnyObject? | |
| // 3 | |
| public var wrappedValue: Value? { | |
| return underlyingValue as? Value | |
| } | |
| public init(container: DIContainer = DependencyContainer.shared, key: String? = nil, mode: ResolveMode = .shared) { | |
| // 4 | |
| if let key = key { | |
| self.underlyingValue = container.resolve(key: key, mode: mode) | |
| } else { | |
| self.underlyingValue = container.resolve(type: Value.self, mode: mode) as AnyObject | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class ViewControllerInjected: UIViewController { | |
| @Inject | |
| var networkingLayer: NetworkingLayer | |
| @Inject(key: "MockedNetworkingLayer") | |
| var mockedNetworkingLayer: NetworkingLayer | |
| @Inject | |
| var user: User | |
| @Inject(container: networkContainer) | |
| var fromNetworkContainer: NetworkingLayer | |
| // 1 | |
| @WeakInject | |
| var networkingLayer2: NetworkingLayer? | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment