Last active
September 29, 2023 01:01
-
-
Save DanielCardonaRojas/5a3ca153a43300ef0e259a10ad0beb36 to your computer and use it in GitHub Desktop.
Dependency injection for Swift projects
This file contains 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 class DependencyResolver | |
{ | |
public typealias Factory<T> = () -> T | |
public static let shared = DependencyResolver() | |
var factoryDict: TypeDict<Factory<Any>> = [:] | |
public init() {} | |
/// Register a dependency such that on each retrieval the caller gets a new instance | |
public func registerFactory<Component>(ofType dependencyType: Component.Type, _ factory: @escaping Factory<Component>) | |
{ | |
factoryDict[dependencyType.self] = factory | |
} | |
/// Register a dependency such that on each retrieval the caller gets the same instance, allowing state preservation | |
/// Note: The type must be a class or reference type to ensure that this is effectively a singleton, otherwise a crash will occur | |
public func registerSingleton<Component>(ofType dependencyType: Component.Type, _ singleton: Component) | |
{ | |
let ref = (singleton as Any) | |
let runtimeType = type(of: ref) | |
guard runtimeType is AnyClass | |
else | |
{ | |
fatalError("Dependency '\(Component.self)' must be a reference type to be registered as singleton") | |
} | |
factoryDict[dependencyType.self] = { singleton } | |
} | |
/// Register a dependency such that on each retrieval the caller gets the same instance, allowing state preservation. | |
/// Note: The type must be a class or reference type to ensure that this is effectively a singleton, otherwise an exception will be thrown | |
public func registerSingletonSafe<Component>(ofType dependencyType: Component.Type, _ singleton: Component) throws | |
{ | |
let ref = (singleton as Any) | |
let runtimeType = type(of: ref) | |
guard runtimeType is AnyClass | |
else | |
{ | |
throw DependencyError.failedToRegister(Component.self) | |
} | |
factoryDict[dependencyType.self] = { singleton } | |
} | |
/// Attempts to extract the dependency for a given type. If the type was not registered previously this will cause a fatalError | |
public func resolve<Component>(_: Component.Type) -> Component | |
{ | |
guard let component = factoryDict[Component.self]?() as? Component | |
else | |
{ | |
fatalError("Dependency '\(Component.self)' not resolved!") | |
} | |
return component | |
} | |
/// Attempts to extract the dependency for a given type. If the type was not registered previously an exception will be thrown | |
public func resolveSafe<Component>(_: Component.Type) throws -> Component | |
{ | |
guard let component = factoryDict[Component.self]?() as? Component | |
else | |
{ | |
throw DependencyError.failedToResolve(Component.self) | |
} | |
return component | |
} | |
public func canResolveDependency<Component>(forType dependencyType: Component.Type) -> Bool | |
{ | |
factoryDict[dependencyType] != nil | |
} | |
} | |
enum DependencyError: Error | |
{ | |
case failedToResolve(Any.Type) | |
case failedToRegister(Any.Type) | |
} | |
@propertyWrapper | |
public struct Inject<Component> | |
{ | |
var component: Component | |
public init() | |
{ | |
component = DependencyResolver.shared.resolve(Component.self) | |
} | |
public var wrappedValue: Component | |
{ | |
get { return component } | |
set { component = newValue } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment