Dependency Key Patterns
Organize DependencyKey patterns for safe dependency resolution with WeaveDI's actual implementation.
Overview
WeaveDI's dependency system is built around KeyPath-based registration and type-safe resolution. This guide covers the actual API patterns available in the framework, based on the real source code implementation.
Core API Patterns
1. UnifiedDI Registration Pattern
The primary API for dependency registration and resolution:
swift
import WeaveDI
// Basic registration with immediate return
let userService = UnifiedDI.register(UserService.self) {
UserServiceImpl()
}
// KeyPath-based registration
let repository = UnifiedDI.register(\.userRepository) {
UserRepositoryImpl()
}
// Async registration
let asyncService = await UnifiedDI.registerAsync(AsyncService.self) {
await AsyncServiceImpl()
}
2. Safe Resolution Patterns
swift
// Safe resolution (returns optional)
let service = UnifiedDI.resolve(UserService.self)
// Required resolution (fatalError on failure)
let requiredService = UnifiedDI.requireResolve(UserService.self)
// Resolution with default fallback
let serviceWithDefault = UnifiedDI.resolve(UserService.self, default: DefaultUserService())
// Async resolution
let asyncResult = await UnifiedDI.resolveAsync(AsyncService.self)
Advanced Patterns
3. WeaveDI.Container Bootstrap Pattern
The actual bootstrap API from the source code:
swift
// Synchronous bootstrap
await WeaveDI.Container.bootstrap { container in
_ = container.register(UserService.self) { UserServiceImpl() }
_ = container.register(DataRepository.self) { DataRepositoryImpl() }
}
// Asynchronous bootstrap
let success = await WeaveDI.Container.bootstrapAsync { container in
let config = await RemoteConfig.fetch()
_ = container.register(Configuration.self) { config }
}
// Mixed bootstrap (sync + async)
await WeaveDI.Container.bootstrapMixed(
sync: { container in
_ = container.register(Logger.self) { LoggerImpl() }
},
async: { container in
let database = await Database.initialize()
_ = container.register(Database.self) { database }
}
)
// Conditional bootstrap
let wasNeeded = await WeaveDI.Container.bootstrapIfNeeded { container in
_ = container.register(DevService.self) { DevServiceImpl() }
}
4. Property Wrapper Patterns
Based on the actual PropertyWrapper implementations:
swift
class ViewController {
// Optional injection (safe)
@Inject var userService: UserService?
// Required injection (crashes if not registered)
@Inject var logger: Logger
// KeyPath-based injection
@Inject(\.dataRepository) var repository: DataRepository?
// Factory pattern (new instance each time)
@Factory var temporaryCache: TemporaryCache
// Safe injection with error handling
@SafeInject var riskService: RiskService
func handleSafeInjection() {
switch riskService {
case .success(let service):
service.doWork()
case .failure(let error):
print("Injection failed: \(error)")
}
// Or with throwing
do {
let service = try riskService.getValue()
service.doWork()
} catch {
print("Failed to get service: \(error)")
}
}
}
5. SimpleKeyPathRegistry Pattern
For more control over registration:
swift
// Basic KeyPath registration
SimpleKeyPathRegistry.register(\.userService) {
UserServiceImpl()
}
// Conditional registration
SimpleKeyPathRegistry.registerIf(\.debugService, condition: isDebugMode) {
DebugServiceImpl()
}
// Environment-specific registration
#if DEBUG
SimpleKeyPathRegistry.registerIfDebug(\.mockService) {
MockServiceImpl()
}
#else
SimpleKeyPathRegistry.registerIfRelease(\.productionService) {
ProductionServiceImpl()
}
#endif
6. SafeDependencyRegister Helpers
swift
// Safe resolution with fallback
let service = SafeDependencyRegister.resolveWithFallback(\.userService) {
DefaultUserService()
}
// Optional safe resolution
let optionalService = SafeDependencyRegister.safeResolve(\.optionalService)
Module System Patterns
7. Module Registration
Based on the actual Module struct:
swift
// Create and register a module
let userModule = Module(UserService.self) {
UserServiceImpl()
}
await WeaveDI.Container.shared.register(userModule)
// Register with error handling
do {
await userModule.registerThrowing()
} catch {
print("Module registration failed: \(error)")
}
8. ModuleFactory Pattern
Using the real ModuleFactory protocol:
swift
struct UserModuleFactory: ModuleFactory {
var registerModule = RegisterModule()
var definitions: [@Sendable () -> Module] = []
mutating func setup() {
definitions.append {
registerModule.makeModule(UserService.self) {
UserServiceImpl()
}
}
definitions.append {
registerModule.makeUseCaseWithRepository(
UserUseCase.self,
repositoryProtocol: UserRepository.self,
repositoryFallback: DefaultUserRepository(),
factory: { repository in
UserUseCaseImpl(repository: repository)
}
)()
}
}
func makeAllModules() -> [Module] {
definitions.map { $0() }
}
}
// Usage
var factory = UserModuleFactory()
factory.setup()
let modules = factory.makeAllModules()
for module in modules {
await module.register()
}
9. ModuleFactoryManager Pattern
swift
let manager = ModuleFactoryManager(
repositoryFactory: RepositoryModuleFactory(),
useCaseFactory: UseCaseModuleFactory(),
scopeFactory: ScopeModuleFactory()
)
await manager.registerAll()
Scope Management Patterns
10. ScopeContext Usage
swift
// Set current scope
ScopeContext.shared.setCurrent(.screen, id: "userProfile")
// Get current scope ID
let currentScreenID = ScopeContext.shared.currentID(for: .screen)
// Scope kinds available
let scopes: [ScopeKind] = [.singleton, .screen, .session, .request]
Best Practices
Type Safety
swift
// ✅ Good: Use KeyPath for type safety
UnifiedDI.register(\.userService) { UserServiceImpl() }
// ✅ Good: Handle optional results
if let service = UnifiedDI.resolve(UserService.self) {
service.performAction()
}
// ❌ Avoid: Force unwrapping
let service = UnifiedDI.resolve(UserService.self)! // Dangerous!
Error Handling
swift
// ✅ Good: Use SafeInject for error-prone dependencies
@SafeInject var networkService: NetworkService
func handleNetworkOperation() {
do {
let service = try networkService.getValue()
await service.fetchData()
} catch SafeInjectError.notRegistered {
// Handle missing dependency
showOfflineMode()
} catch {
// Handle other errors
showError(error)
}
}
Performance Optimization
swift
// Register expensive services as singletons
UnifiedDI.register(ExpensiveService.self) {
ExpensiveServiceImpl() // Created once
}
// Use Factory for lightweight, stateless services
@Factory var dateFormatter: DateFormatter // New instance each time
Testing Support
swift
#if DEBUG
extension WeaveDI.Container {
static func setupForTesting() async {
await WeaveDI.Container.releaseAll() // Clear all dependencies
await WeaveDI.Container.bootstrap { container in
_ = container.register(UserService.self) { MockUserService() }
_ = container.register(NetworkService.self) { MockNetworkService() }
}
}
}
#endif
Migration from Other DI Frameworks
From Swinject
swift
// Before (Swinject)
container.register(UserServiceProtocol.self) { _ in
UserServiceImpl()
}
// After (WeaveDI)
UnifiedDI.register(UserServiceProtocol.self) {
UserServiceImpl()
}
From Factory
swift
// Before (Factory)
@Injected(\.userService) var userService: UserService
// After (WeaveDI)
@Inject(\.userService) var userService: UserService?
This comprehensive guide covers all the actual patterns available in WeaveDI based on the real source code implementation, ensuring accuracy and practical applicability.