Skip to content

Instantly share code, notes, and snippets.

@minsOne
Last active July 27, 2022 14:48
Show Gist options
  • Select an option

  • Save minsOne/893b5ba0523b115579ae94012bc51248 to your computer and use it in GitHub Desktop.

Select an option

Save minsOne/893b5ba0523b115579ae94012bc51248 to your computer and use it in GitHub Desktop.
DI Container Sample
//
// AlphaApp.swift
// Shared
//
// Created by damon.ahn on 2022/07/15.
//
import SwiftUI
@main
struct AlphaApp: App {
var body: some Scene {
WindowGroup {
ContentView().task {
// RegisterHelper().register()
let sample = InjectSample()
sample.register()
sample.resolve()
}
}
}
}
public class InjectSample {
// @Inject1
// var aaaa: AAAAA
@Inject1
var cas: CASProtocol
@Inject2
var casfactory: CASFactory
@Inject3(\.caslogFactory)
var factory1: CasLogFactoryProtocol
@Inject4(CasLogFactoryKey.self)
var factory2: CasLogFactoryProtocol
func register() {
let dependencies = Dependencies {
Module(CASProtocol.self) { CASLog() }
Module(CASFactory.self) { CASFactory(CASFactoryImpl()) }
}
dependencies.build()
Container.share
.register(CasLogFactoryKey.self) { CasLogFactoryImpl () }
}
func resolve() {
print("====== Inject1 ======")
cas.printing()
print("====== Inject2 ======")
casfactory.printing()
print("====== Inject3 ======")
factory1.printing()
print("====== Inject4 ======")
factory2.printing()
}
}
public protocol AAAAA {}
public protocol CASProtocol: Injectable {
func printing()
}
public struct CASLog: CASProtocol {
public func printing() {
print("Printing CASLog")
}
}
public protocol CASFactoryProtocol {
func printing()
}
public struct CASFactoryImpl: CASFactoryProtocol {
public init() {}
public func printing() {
print("Printing CASFactoryImpl")
}
}
public struct CASFactory: Injectable {
public let factory: CASFactoryProtocol
public init(_ factory: CASFactoryProtocol) {
self.factory = factory
}
public func printing() {
factory.printing()
}
}
public protocol CasLogFactoryProtocol {
func printing()
}
public struct CasLogFactoryImpl: CasLogFactoryProtocol {
public func printing() {
print("Printing CasLogFactoryImpl")
}
}
public struct CasLogFactoryKey: InjectionKey {
public typealias Value = CasLogFactoryProtocol
public static var currentValue: Value {
Container.share.resolve(Self.self)
}
}
extension InjectedValues {
var caslogFactory: CasLogFactoryProtocol {
Self[CasLogFactoryKey.self]
}
}
/// Resolves an instance from the dependency injection container.
@propertyWrapper
public class Inject1<Value> {
private let name: String?
private var storage: Value?
public var wrappedValue: Value {
storage ?? {
let value: Value = Dependencies.root.resolve(for: name)
storage = value // Reuse instance for later
return value
}()
}
public init() {
self.name = nil
}
public init(_ name: String) {
self.name = name
}
}
@propertyWrapper
public class Inject2<Value: Injectable> {
private let name: String?
private var storage: Value?
public var wrappedValue: Value {
storage ?? {
let value: Value = Dependencies.root.resolve(for: name)
storage = value // Reuse instance for later
return value
}()
}
public init() {
self.name = nil
}
public init(_ name: String) {
self.name = name
}
}
public protocol Injectable {}
/// A type that contributes to the object graph.
public struct Module {
fileprivate let name: String
fileprivate let resolve: () -> Injectable
public init(_ name: Any.Type, _ resolve: @escaping () -> Injectable) {
self.name = String(describing: name)
self.resolve = resolve
}
}
/// A dependency collection that provides resolutions for object instances.
public class Dependencies {
/// Stored object instance factories.
private var modules: [String: Module] = [:]
public init() {}
deinit { modules.removeAll() }
}
extension Dependencies {
/// Registers a specific type and its instantiating factory.
func add(module: Module) {
modules[module.name] = module
}
/// Resolves through inference and returns an instance of the given type from the current default container.
///
/// If the dependency is not found, an exception will occur.
func resolve<T>(for name: String? = nil) -> T {
let name = name ?? String(describing: T.self)
guard let component: T = modules[name]?.resolve() as? T else {
fatalError("Dependency '\(T.self)' not resolved!")
}
return component
}
/// Composition root container of dependencies.
static var root = Dependencies()
/// Construct dependency resolutions.
public convenience init(@ModuleBuilder _ modules: () -> [Module]) {
self.init()
modules().forEach { add(module: $0) }
}
/// Construct dependency resolution.
public convenience init(@ModuleBuilder _ module: () -> Module) {
self.init()
add(module: module())
}
/// Assigns the current container to the composition root.
public func build() {
// Used later in property wrapper
Self.root = self
}
/// DSL for declaring modules within the container dependency initializer.
@resultBuilder public struct ModuleBuilder {
public static func buildBlock(_ modules: Module...) -> [Module] { modules }
public static func buildBlock(_ module: Module) -> Module { module }
}
}
import Foundation
public protocol InjectionKey {
associatedtype Value
static var currentValue: Self.Value { get }
}
// MARK: - InjectedValues
public struct InjectedValues {
private static var current = InjectedValues()
public static subscript<K>(key: K.Type) -> K.Value where K : InjectionKey {
get { key.currentValue }
}
public static subscript<T>(_ keyPath: KeyPath<InjectedValues, T>) -> T {
get { current[keyPath: keyPath] }
}
}
@propertyWrapper
public class Inject3<Value> {
private let keyPath: KeyPath<InjectedValues, Value>
private var storage: Value?
public var wrappedValue: Value {
storage ?? {
let value: Value = InjectedValues[keyPath]
storage = value // Reuse instance for later
return value
}()
}
public init(_ keyPath: KeyPath<InjectedValues, Value>) {
self.keyPath = keyPath
}
}
@propertyWrapper
public class Inject4<Value> {
private let lazyValue: (() -> Value)
private var storage: Value?
public var wrappedValue: Value {
storage ?? {
let value: Value = lazyValue()
storage = value // Reuse instance for later
return value
}()
}
public init<K>(_ key: K.Type) where K : InjectionKey, Value == K.Value {
lazyValue = {
key.currentValue
}
}
}
public class Container {
public static let share: Container = Container()
var dict = [String: Any]()
init() {}
func register(_ name: Any.Type, instance: () -> Any) {
let name = String(describing: name)
let resolve = instance()
dict[name] = resolve
}
func resolve<T>(_ name: Any.Type) -> T {
let name = String(describing: name)
guard let component = dict[name] as? T else {
fatalError("Dependency '\(T.self)' not resolved!")
}
return component
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment