Skip to content

App DI Integration: AppWeaveDI.Container

A complete guide to implementing enterprise-level Dependency Injection architecture in real-world applications using AppWeaveDI.Container.

Overview

AppWeaveDI.Container is a top-level container class that systematically manages dependency injection across the entire application. Through automated Factory patterns, it efficiently organizes and manages each layer (Repository, UseCase, Service) while supporting Clean Architecture.

Architecture Philosophy

🏗️ Layered Architecture Support

  • Repository Layer: Data access and external system integration
  • UseCase Layer: Business logic and domain rule encapsulation
  • Service Layer: Application services and UI support
  • Automatic Dependency Resolution: Dependencies between layers are automatically injected

🏭 Factory-Based Modularization

  • RepositoryModuleFactory: Bulk management of Repository dependencies
  • UseCaseModuleFactory: Automatic configuration of UseCases integrated with Repositories
  • Extensibility: Easy addition of new factories
  • Type Safety: Compile-time dependency type verification

🔄 Lifecycle Management

  • Lazy Initialization: Module creation only when actually needed
  • Memory Efficiency: Dependencies not in use are not created

Architecture Diagram

┌─────────────────────────────────────┐
│       AppWeaveDI.Container          │
│                                     │
└─────────────────┬───────────────────┘

      ┌───────────┼───────────┐
      │           │           │
┌─────▼─────┐ ┌───▼────┐ ┌───▼────────┐
│Repository │ │UseCase │ │   Other    │
│ Factory   │ │Factory │ │ Factories  │
└───────────┘ └────────┘ └────────────┘
      │           │           │
      └───────────┼───────────┘

┌─────────────────▼───────────────────┐
│        WeaveDI.Container.live       │
│          (Global Registry)          │
└─────────────────────────────────────┘

How It Works

Step 1: Factory Preparation

AppWeaveDI.Container uses the @Factory property wrapper for automatic injection:

swift
@Factory(\.repositoryFactory)
var repositoryFactory: RepositoryModuleFactory

@Factory(\.useCaseFactory)
var useCaseFactory: UseCaseModuleFactory

@Factory(\.scopeFactory)
var scopeFactory: ScopeModuleFactory

Step 2: Module Registration

swift
await AppWeaveDI.Container.shared.registerDependencies { container in
    // Register Repository modules
    container.register(UserRepositoryModule())

    // Register UseCase modules
    container.register(UserUseCaseModule())

    // Register Service modules
    container.register(UserServiceModule())
}

Internal Process:

  1. Repository factory creates all Repository modules
  2. UseCase factory creates UseCases connected with Repositories
  3. All modules are registered in parallel to WeaveDI.Container.live

Step 3: Dependency Usage

swift
// Use registered dependencies anywhere
let userService = WeaveDI.Container.live.resolve(UserServiceProtocol.self)
let userUseCase = WeaveDI.Container.live.resolve(UserUseCaseProtocol.self)

Compatibility and Environment Support

Swift Version Compatibility

  • Swift 5.9+ & iOS 17.0+: Actor-based optimized implementation
  • Swift 5.8 & iOS 16.0+: Compatible mode with same functionality
  • Earlier versions: Fallback implementation maintaining core features

Concurrency Support

  • Swift Concurrency: Full support for async/await patterns
  • GCD Compatible: Works with existing DispatchQueue based code
  • Thread Safety: All operations are thread-safe

Basic Usage

Simple Application Setup

swift
@main
struct MyApp {
    static func main() async {
        await AppWeaveDI.Container.shared.registerDependencies { container in
            // Repository modules
            container.register(UserRepositoryModule())
            container.register(OrderRepositoryModule())

            // UseCase modules
            container.register(UserUseCaseModule())
            container.register(OrderUseCaseModule())

            // Service modules
            container.register(UserServiceModule())
        }

        // Use registered dependencies
        let useCase: UserUseCaseProtocol = WeaveDI.Container.live.resolveOrDefault(
            UserUseCaseProtocol.self,
            default: UserUseCase(userRepo: UserRepository())
        )

        print("Loaded user profile: \(await useCase.loadUserProfile().displayName)")
    }
}

Factory Pattern Extensions

Repository Factory Extension

swift
extension RepositoryModuleFactory {
    public mutating func registerDefaultDefinitions() {
        let registerModuleCopy = registerModule
        repositoryDefinitions = [
            // User Repository
            registerModuleCopy.makeDependency(UserRepositoryProtocol.self) {
                UserRepositoryImpl(
                    networkService: WeaveDI.Container.live.resolve(NetworkService.self)!,
                    cacheService: WeaveDI.Container.live.resolve(CacheService.self)!
                )
            },

            // Auth Repository
            registerModuleCopy.makeDependency(AuthRepositoryProtocol.self) {
                AuthRepositoryImpl(
                    keychain: KeychainService(),
                    networkService: WeaveDI.Container.live.resolve(NetworkService.self)!
                )
            },

            // Order Repository
            registerModuleCopy.makeDependency(OrderRepositoryProtocol.self) {
                OrderRepositoryImpl(
                    database: WeaveDI.Container.live.resolve(DatabaseService.self)!
                )
            }
        ]
    }
}

UseCase Factory Extension

swift
extension UseCaseModuleFactory {
    public var useCaseDefinitions: [() -> Module] {
        [
            // Auth UseCase with Repository dependency
            registerModule.makeUseCaseWithRepository(
                AuthUseCaseProtocol.self,
                repositoryProtocol: AuthRepositoryProtocol.self,
                repositoryFallback: DefaultAuthRepository()
            ) { repo in
                AuthUseCase(
                    repository: repo,
                    validator: AuthValidator(),
                    logger: WeaveDI.Container.live.resolve(LoggerProtocol.self)!
                )
            },

            // User UseCase with Repository dependency
            registerModule.makeUseCaseWithRepository(
                UserUseCaseProtocol.self,
                repositoryProtocol: UserRepositoryProtocol.self,
                repositoryFallback: DefaultUserRepository()
            ) { repo in
                UserUseCase(
                    repository: repo,
                    authUseCase: WeaveDI.Container.live.resolve(AuthUseCaseProtocol.self)!,
                    validator: UserValidator()
                )
            },

            // Order UseCase with multiple dependencies
            registerModule.makeUseCaseWithRepository(
                OrderUseCaseProtocol.self,
                repositoryProtocol: OrderRepositoryProtocol.self,
                repositoryFallback: DefaultOrderRepository()
            ) { repo in
                OrderUseCase(
                    repository: repo,
                    userUseCase: WeaveDI.Container.live.resolve(UserUseCaseProtocol.self)!,
                    paymentService: WeaveDI.Container.live.resolve(PaymentService.self)!
                )
            }
        ]
    }
}

Advanced Usage Patterns

SwiftUI App Integration

swift
@main
struct TestApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    init() {
        registerDependencies()
    }

    var body: some Scene {
        WindowGroup {
            let store = Store(initialState: AppReducer.State()) {
                AppReducer()._printChanges()
            }
            AppView(store: store)
        }
    }

    private func registerDependencies() {
        Task {
            await AppWeaveDI.Container.shared.registerDependencies { container in
                // Repository layer setup
                var repoFactory = AppWeaveDI.Container.shared.repositoryFactory
                repoFactory.registerDefaultDefinitions()

                await repoFactory.makeAllModules().asyncForEach { module in
                    await container.register(module)
                }

                // UseCase layer setup
                let useCaseFactory = AppWeaveDI.Container.shared.useCaseFactory
                await useCaseFactory.makeAllModules().asyncForEach { module in
                    await container.register(module)
                }

                // Service layer setup
                await registerServiceModules(container)
            }
        }
    }

    private func registerServiceModules(_ container: Container) async {
        // Register application services
        await container.register(AnalyticsServiceModule())
        await container.register(NotificationServiceModule())
        await container.register(LocationServiceModule())
    }
}

UIKit (AppDelegate) Integration

swift
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // Configure AppWeaveDI.Container for UIKit apps
        Task {
            await AppWeaveDI.Container.shared.registerDependencies { container in
                // Core infrastructure
                await setupCoreInfrastructure(container)

                // Feature modules
                await setupFeatureModules(container)

                // UI-specific services
                await setupUIServices(container)
            }
        }

        return true
    }

    private func setupCoreInfrastructure(_ container: Container) async {
        // Database setup
        let database = try! await Database.initialize()
        await container.register(DatabaseServiceModule(database: database))

        // Network setup
        await container.register(NetworkServiceModule())

        // Logging setup
        await container.register(LoggingServiceModule())
    }

    private func setupFeatureModules(_ container: Container) async {
        // Repository factory
        var repoFactory = AppWeaveDI.Container.shared.repositoryFactory
        repoFactory.registerDefaultDefinitions()

        let repoModules = await repoFactory.makeAllModules()
        for module in repoModules {
            await container.register(module)
        }

        // UseCase factory
        let useCaseFactory = AppWeaveDI.Container.shared.useCaseFactory
        let useCaseModules = await useCaseFactory.makeAllModules()
        for module in useCaseModules {
            await container.register(module)
        }
    }

    private func setupUIServices(_ container: Container) async {
        // UI-specific services
        await container.register(ViewControllerFactoryModule())
        await container.register(NavigationServiceModule())
        await container.register(AlertServiceModule())
    }
}

Using ContainerRegister

For type-safe dependency access, use the ContainerRegister pattern:

swift
extension WeaveDI.Container {
    var authUseCase: AuthUseCaseProtocol? {
        ContainerRegister(\.authUseCase).wrappedValue
    }

    var userService: UserServiceProtocol? {
        ContainerRegister(\.userService).wrappedValue
    }

    var orderRepository: OrderRepositoryProtocol? {
        ContainerRegister(\.orderRepository).wrappedValue
    }
}

// Usage example
class AuthenticationViewModel: ObservableObject {
    @Published var isAuthenticated = false

    private let authUseCase: AuthUseCaseProtocol = ContainerRegister(\.authUseCase).wrappedValue

    func login(email: String, password: String) async {
        do {
            let result = try await authUseCase.login(email: email, password: password)
            await MainActor.run {
                self.isAuthenticated = result.isSuccess
            }
        } catch {
            // Handle authentication failure
            print("Login failed: \(error)")
        }
    }
}

Complex Enterprise Architecture

swift
class EnterpriseAppBootstrap {
    static func configure() async {
        await AppWeaveDI.Container.shared.registerDependencies { container in
            // Infrastructure layer
            await setupInfrastructure(container)

            // Data layer
            await setupDataLayer(container)

            // Domain layer
            await setupDomainLayer(container)

            // Application layer
            await setupApplicationLayer(container)

            // Presentation layer
            await setupPresentationLayer(container)
        }
    }

    private static func setupInfrastructure(_ container: Container) async {
        // Core infrastructure services
        await container.register(NetworkServiceModule())
        await container.register(DatabaseServiceModule())
        await container.register(CacheServiceModule())
        await container.register(SecurityServiceModule())
        await container.register(LoggingServiceModule())
    }

    private static func setupDataLayer(_ container: Container) async {
        // Repository factory setup
        var repoFactory = AppWeaveDI.Container.shared.repositoryFactory
        repoFactory.registerDefaultDefinitions()

        let modules = await repoFactory.makeAllModules()
        for module in modules {
            await container.register(module)
        }
    }

    private static func setupDomainLayer(_ container: Container) async {
        // UseCase factory setup
        let useCaseFactory = AppWeaveDI.Container.shared.useCaseFactory
        let modules = await useCaseFactory.makeAllModules()

        for module in modules {
            await container.register(module)
        }
    }

    private static func setupApplicationLayer(_ container: Container) async {
        // Application services
        await container.register(AuthenticationServiceModule())
        await container.register(UserManagementServiceModule())
        await container.register(OrderProcessingServiceModule())
        await container.register(PaymentServiceModule())
    }

    private static func setupPresentationLayer(_ container: Container) async {
        // ViewModels and Presenters
        await container.register(UserViewModelModule())
        await container.register(OrderViewModelModule())
        await container.register(PaymentViewModelModule())
    }
}

Performance Optimization

Automatic Optimization Configuration

AppWeaveDI.Container automatically configures performance optimization:

swift
public func registerDependencies(
    registerModules: @escaping @Sendable (Container) async -> Void
) async {
    // Enable runtime optimization and minimize logging for performance-sensitive builds
    UnifiedDI.configureOptimization(debounceMs: 100, threshold: 10, realTimeUpdate: true)
    UnifiedDI.setAutoOptimization(true)
    UnifiedDI.setLogLevel(.errors)

    // ... rest of registration logic
}

Parallel Module Registration

For optimal performance, the container registers modules in parallel:

swift
// All modules registered concurrently
await container.register(module1)  // Parallel
await container.register(module2)  // Parallel
await container.register(module3)  // Parallel
await container.build()            // Wait for all to complete

Memory Management

AppWeaveDI.Container implements efficient memory management:

swift
// Lazy initialization - created only when needed
@Factory(\.repositoryFactory)
var repositoryFactory: RepositoryModuleFactory  // Created on first access

// Automatic cleanup of unused dependencies
private func cleanupUnusedDependencies() {
    // Internal optimization logic
}

Testing Strategies

Unit Testing with AppWeaveDI.Container

swift
class AppWeaveDIContainerTests: XCTestCase {
    var container: AppWeaveDI.Container!

    override func setUp() async throws {
        try await super.setUp()
        container = AppWeaveDI.Container()
    }

    func testRepositoryFactoryRegistration() async {
        await container.registerDependencies { container in
            var repoFactory = self.container.repositoryFactory
            repoFactory.registerDefaultDefinitions()

            let modules = await repoFactory.makeAllModules()
            XCTAssertFalse(modules.isEmpty)

            for module in modules {
                await container.register(module)
            }
        }

        // Verify registration
        let userRepo = WeaveDI.Container.live.resolve(UserRepositoryProtocol.self)
        XCTAssertNotNil(userRepo)
    }

    func testUseCaseFactoryRegistration() async {
        await container.registerDependencies { container in
            // Setup Repository first
            var repoFactory = self.container.repositoryFactory
            repoFactory.registerDefaultDefinitions()
            await repoFactory.makeAllModules().asyncForEach { module in
                await container.register(module)
            }

            // Then setup UseCase
            let useCaseFactory = self.container.useCaseFactory
            await useCaseFactory.makeAllModules().asyncForEach { module in
                await container.register(module)
            }
        }

        // Verify UseCase registration
        let authUseCase = WeaveDI.Container.live.resolve(AuthUseCaseProtocol.self)
        XCTAssertNotNil(authUseCase)
    }
}

Integration Testing

swift
class IntegrationTests: XCTestCase {
    override func setUp() async throws {
        // Initialize container for each test
        WeaveDI.Container.live = WeaveDI.Container()

        await AppWeaveDI.Container.shared.registerDependencies { container in
            // Register test-specific dependencies
            await self.registerTestDependencies(container)
        }
    }

    private func registerTestDependencies(_ container: Container) async {
        // Mock repositories for integration tests
        await container.register(MockUserRepositoryModule())
        await container.register(MockAuthRepositoryModule())

        // Use real UseCases for integration tests
        let useCaseFactory = AppWeaveDI.Container.shared.useCaseFactory
        await useCaseFactory.makeAllModules().asyncForEach { module in
            await container.register(module)
        }
    }

    func testUserAuthenticationFlow() async throws {
        let authUseCase = WeaveDI.Container.live.resolve(AuthUseCaseProtocol.self)!
        let userUseCase = WeaveDI.Container.live.resolve(UserUseCaseProtocol.self)!

        // Test complete authentication flow
        let authResult = try await authUseCase.login(email: "test@example.com", password: "password")
        XCTAssertTrue(authResult.isSuccess)

        let userProfile = try await userUseCase.loadUserProfile()
        XCTAssertNotNil(userProfile)
    }
}

Best Practices

1) Organize by Feature Modules

swift
// Feature-based module organization
struct UserFeatureModule {
    static func register(_ container: Container) async {
        // User-related repositories
        await container.register(UserRepositoryModule())
        await container.register(UserPreferencesRepositoryModule())

        // User-related use cases
        await container.register(UserUseCaseModule())
        await container.register(UserPreferencesUseCaseModule())

        // User-related services
        await container.register(UserServiceModule())
    }
}

struct OrderFeatureModule {
    static func register(_ container: Container) async {
        await container.register(OrderRepositoryModule())
        await container.register(OrderUseCaseModule())
        await container.register(OrderServiceModule())
    }
}

2) Environment-Specific Configuration

swift
extension AppWeaveDI.Container {
    func registerDependenciesForEnvironment(_ environment: AppEnvironment) async {
        await registerDependencies { container in
            switch environment {
            case .development:
                await self.registerDevelopmentDependencies(container)
            case .staging:
                await self.registerStagingDependencies(container)
            case .production:
                await self.registerProductionDependencies(container)
            }
        }
    }

    private func registerDevelopmentDependencies(_ container: Container) async {
        // Development environment specific implementations
        await container.register(MockNetworkServiceModule())
        await container.register(InMemoryDatabaseModule())
        await container.register(DetailedLoggingModule())
    }

    private func registerProductionDependencies(_ container: Container) async {
        // Production environment implementations
        await container.register(ProductionNetworkServiceModule())
        await container.register(SQLiteDatabaseModule())
        await container.register(OptimizedLoggingModule())
    }
}

3) Gradual Migration Strategy

swift
class LegacyAppMigration {
    static func migrateToAppWeaveDI() async {
        await AppWeaveDI.Container.shared.registerDependencies { container in
            // Gradually migrate existing dependencies
            await migrateCoreServices(container)
            await migrateUserServices(container)
            await migrateOrderServices(container)
        }
    }

    private static func migrateCoreServices(_ container: Container) async {
        // Reuse existing instances if necessary
        if let existingLogger = LegacyServiceLocator.shared.logger {
            await container.register(ExistingLoggerModule(logger: existingLogger))
        } else {
            await container.register(NewLoggerModule())
        }
    }
}

Discussion

  • AppWeaveDI.Container serves as the single entry point for dependency management.
  • Once modules are registered during app initialization, dependency objects can be created and accessed quickly and reliably at runtime.
  • The internal Container executes all registered modules in parallel for performance optimization.
  • Factory patterns systematically manage Repository, UseCase, and Scope layers.
  • Through automatic optimization configuration, optimal performance is provided with default settings.

See Also

Released under the MIT License.