Skip to content

Instantly share code, notes, and snippets.

@DanielCardonaRojas
Last active September 29, 2023 01:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanielCardonaRojas/5a3ca153a43300ef0e259a10ad0beb36 to your computer and use it in GitHub Desktop.
Save DanielCardonaRojas/5a3ca153a43300ef0e259a10ad0beb36 to your computer and use it in GitHub Desktop.
Dependency injection for Swift projects
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