From 2280142fe1eb8fe61724ab748dc337f89cf7b076 Mon Sep 17 00:00:00 2001 From: Brendan Szymanski Date: Sun, 14 Jun 2026 16:16:01 -0400 Subject: [PATCH] Add DIContainer with observer registry --- Sources/LuminateDI/DIContainer.swift | 69 ++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 Sources/LuminateDI/DIContainer.swift diff --git a/Sources/LuminateDI/DIContainer.swift b/Sources/LuminateDI/DIContainer.swift new file mode 100644 index 0000000..9698aa3 --- /dev/null +++ b/Sources/LuminateDI/DIContainer.swift @@ -0,0 +1,69 @@ +import Foundation + +public final class DIContainer { + + public static let shared = DIContainer() + public private(set) var values = InjectionValues() + private var observers: [AnyKeyPath: [UUID: () -> Void]] = [:] + private let lock = NSLock() + + private init() {} + + public func register( + _ keyPath: WritableKeyPath, + value: T + ) { + lock.withLock { + values[keyPath: keyPath] = value + } + notifyObservers(for: keyPath) + } + + public func resolve( + _ keyPath: KeyPath + ) -> T { + lock.withLock { + guard let value = values[keyPath: keyPath] else { + fatalError( + "DIContainer: No value registered for \(keyPath). " + + "Call DIContainer.shared.register(\\.key, value:) during app startup." + ) + } + return value + } + } + + @discardableResult + func addObserver( + for keyPath: KeyPath, + handler: @escaping () -> Void + ) -> UUID { + let id = UUID() + lock.withLock { + observers[keyPath, default: [:]][id] = handler + } + return id + } + + func removeObserver(_ id: UUID) { + lock.withLock { + for keyPath in observers.keys { + observers[keyPath]?.removeValue(forKey: id) + } + } + } + + private func notifyObservers(for keyPath: AnyKeyPath) { + let handlers: [() -> Void] = lock.withLock { + Array(observers[keyPath]?.values ?? []) + } + handlers.forEach { $0() } + } + + public func reset() { + lock.withLock { + values = InjectionValues() + observers.removeAll() + } + } +}