UnifiedDI
Overview
UnifiedDI is a modern and intuitive dependency injection API. It focuses on core functionality while removing complex features, making it easy to understand and use.
Design Philosophy
- Simplicity First: Clear API over complex features
- Type Safety: All errors verified at compile time
- Intuitive Usage: API that's self-explanatory from the code
Basic Usage
// 1. Register and use immediately
let repository = UnifiedDI.register(UserRepository.self) {
UserRepositoryImpl()
}
// 2. Resolve later
let service = UnifiedDI.resolve(UserService.self)
// 3. Required dependency (crashes if not found)
let logger = UnifiedDI.requireResolve(Logger.self)Core API
Registration Methods
register(_:factory:)
Register a dependency and return the created instance immediately (recommended approach).
public static func register<T>(
_ type: T.Type,
factory: @escaping @Sendable () -> T
) -> T where T: SendableUsage:
let repository = UnifiedDI.register(UserRepository.self) {
UserRepositoryImpl()
}
// repository is immediately available for useregisterAsync(_:factory:)
Register dependencies asynchronously using @DIContainerActor for thread-safe registration.
public static func registerAsync<T>(
_ type: T.Type,
factory: @escaping @Sendable () async -> T
) async -> T where T: SendableUsage:
Task {
let instance = await UnifiedDI.registerAsync(UserService.self) {
UserServiceImpl()
}
// instance is immediately available for use
}Resolution Methods
resolve(_:)
Safely resolve a dependency, returning nil if not registered.
public static func resolve<T>(_ type: T.Type) -> T? where T: SendableUsage:
if let service = UnifiedDI.resolve(UserService.self) {
// Use service
} else {
// Handle fallback logic
}resolveAsync(_:)
Asynchronously resolve dependencies using @DIContainerActor.
public static func resolveAsync<T>(_ type: T.Type) async -> T? where T: SendableUsage:
Task {
if let service = await UnifiedDI.resolveAsync(UserService.self) {
// Use service
}
}requireResolve(_:)
Resolve required dependencies, crashing with a clear error message if not found.
public static func requireResolve<T>(_ type: T.Type) -> T where T: Sendable⚠️ Note: Use resolve(_:) in production environments for safer handling.
Usage:
let logger = UnifiedDI.requireResolve(Logger.self)
// logger is always a valid instanceresolve(_:default:)
Resolve dependencies with a fallback default value (always succeeds).
public static func resolve<T>(
_ type: T.Type,
default defaultValue: @autoclosure () -> T
) -> T where T: SendableUsage:
let logger = UnifiedDI.resolve(Logger.self, default: ConsoleLogger())
// logger is always a valid instanceManagement Methods
release(_:)
Remove a specific dependency from the container.
public static func release<T>(_ type: T.Type) where T: SendableUsage:
UnifiedDI.release(UserService.self)
// Subsequent resolve calls will return nilreleaseAll()
Remove all registered dependencies (primarily for testing).
public static func releaseAll()⚠️ Note: Should only be called from the main thread.
Usage:
// In test setUp
override func setUp() {
super.setUp()
UnifiedDI.releaseAll()
}Advanced Features
Performance Optimization
UnifiedDI includes built-in performance optimization features:
// Enable performance tracking (debug mode only)
#if DEBUG && DI_MONITORING_ENABLED
UnifiedDI.enableOptimization()
let stats = await UnifiedDI.getPerformanceStats()
#endifComponent Diagnostics
Automatic detection of configuration issues:
let diagnostics = UnifiedDI.analyzeComponentMetadata()
if !diagnostics.issues.isEmpty {
print("⚠️ Configuration issues found:")
for issue in diagnostics.issues {
print(" - \(issue.type): \(issue.detail ?? "")")
}
}Integration Examples
SwiftUI Integration
import SwiftUI
struct ContentView: View {
private let userService = UnifiedDI.resolve(
UserService.self,
default: MockUserService()
)
var body: some View {
Text("User: \(userService.currentUser.name)")
}
}TCA Integration
import ComposableArchitecture
struct AppFeature: Reducer {
struct State: Equatable {
// State definition
}
enum Action {
// Action definition
}
var body: some ReducerOf<Self> {
Reduce { state, action in
let userService = UnifiedDI.requireResolve(UserService.self)
// Use userService
return .none
}
}
}Testing Setup
import XCTest
import WeaveDI
class UserServiceTests: XCTestCase {
override func setUp() {
super.setUp()
// Clear previous registrations
UnifiedDI.releaseAll()
// Register test dependencies
_ = UnifiedDI.register(UserRepository.self) {
MockUserRepository()
}
_ = UnifiedDI.register(UserService.self) {
UserServiceImpl(
repository: UnifiedDI.requireResolve(UserRepository.self)
)
}
}
func testUserCreation() {
let service = UnifiedDI.requireResolve(UserService.self)
let user = service.createUser(name: "Test User")
XCTAssertEqual(user.name, "Test User")
}
}Error Handling
Common Error Patterns
// ❌ Avoid: This will crash if not registered
let service = UnifiedDI.requireResolve(UnregisteredService.self)
// ✅ Better: Safe resolution with fallback
let service = UnifiedDI.resolve(UnregisteredService.self) ?? DefaultService()
// ✅ Best: Resolution with default value
let service = UnifiedDI.resolve(UnregisteredService.self, default: DefaultService())Debug Information
#if DEBUG
// Check if a dependency is registered
if UnifiedDI.resolve(SomeService.self) == nil {
print("⚠️ SomeService is not registered")
}
// Analyze configuration issues
let diagnostics = UnifiedDI.analyzeComponentMetadata()
for issue in diagnostics.issues {
print("🔍 Issue: \(issue.type) - \(issue.detail ?? "")")
}
#endifBest Practices
1. Registration Order
Register dependencies in dependency order (dependencies first):
// ✅ Good: Register dependencies first
_ = UnifiedDI.register(APIClient.self) {
APIClientImpl()
}
_ = UnifiedDI.register(UserRepository.self) {
UserRepositoryImpl(
apiClient: UnifiedDI.requireResolve(APIClient.self)
)
}
_ = UnifiedDI.register(UserService.self) {
UserServiceImpl(
repository: UnifiedDI.requireResolve(UserRepository.self)
)
}2. Use Safe Resolution in Production
// ✅ Production: Safe resolution
guard let service = UnifiedDI.resolve(CriticalService.self) else {
// Handle gracefully
return
}
// ✅ Development: Fail fast for debugging
#if DEBUG
let service = UnifiedDI.requireResolve(CriticalService.self)
#else
guard let service = UnifiedDI.resolve(CriticalService.self) else {
// Fallback logic
return
}
#endif3. Centralized Registration
enum DependencyContainer {
static func registerAll() {
registerNetworking()
registerRepositories()
registerServices()
}
private static func registerNetworking() {
_ = UnifiedDI.register(HTTPClient.self) {
URLSessionHTTPClient()
}
_ = UnifiedDI.register(APIClient.self) {
APIClientImpl(
httpClient: UnifiedDI.requireResolve(HTTPClient.self)
)
}
}
private static func registerRepositories() {
_ = UnifiedDI.register(UserRepository.self) {
UserRepositoryImpl(
apiClient: UnifiedDI.requireResolve(APIClient.self)
)
}
}
private static func registerServices() {
_ = UnifiedDI.register(UserService.self) {
UserServiceImpl(
repository: UnifiedDI.requireResolve(UserRepository.self)
)
}
}
}Related APIs
@Injected- Property wrapper for dependency injectionDIAdvanced- Advanced dependency injection featuresComponentDiagnostics- Automatic issue detectionPerformance Optimization- Performance monitoring and optimization
UnifiedDI is the recommended API for dependency injection in WeaveDI v3.3.0+