Skip to content

AppDI Simplification Guide

Overview

WeaveDI 3.2.0 introduces automatic dependency registration, eliminating the need for manual registerRepositories() and registerUseCases() calls. The framework now automatically invokes these methods through the improved registerAllDependencies() system.

What Changed

Before (Manual Registration)

swift
// ❌ Old approach - manual calls required
@main
struct MyApp: App {
    init() {
        Task {
            await WeaveDI.Container.bootstrap { container in
                // Manual registration
                await WeaveDI.Container.registerRepositories()
                await WeaveDI.Container.registerUseCases()
            }
        }
    }
}

After (Automatic Registration)

swift
// ✅ New approach - automatic registration
@main
struct MyApp: App {
    init() {
        WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
            await AppDIManager.shared.registerDefaultDependencies()
        }
    }
}

How It Works

The AppDIManager.shared.registerDefaultDependencies() method automatically calls both registerRepositories() and registerUseCases():

swift
// AppDIManager automatically registers all dependencies
public actor AppDIManager {
    public static let shared = AppDIManager()

    public func registerDefaultDependencies() async {
        // Automatically calls these methods
        await WeaveDI.Container.registerRepositories()
        await WeaveDI.Container.registerUseCases()

        #if DEBUG
        print("✅ AppDIManager.registerDefaultDependencies() completed")
        #endif
    }
}

Module-Based Registration Pattern

Define Your Modules

swift
extension WeaveDI.Container {
    private static let helper = RegisterModule()

    /// 📦 Repository Registration
    static func registerRepositories() async {
        let repositories = [
            helper.exchangeRepositoryModule(),
            helper.userRepositoryModule(),
            // Add more repository modules...
        ]

        await repositories.asyncForEach { module in
            await module.register()
        }
    }

    /// 🔧 UseCase Registration
    static func registerUseCases() async {
        let useCases = [
            helper.exchangeUseCaseModule(),
            helper.userUseCaseModule(),
            // Add more useCase modules...
        ]

        await useCases.asyncForEach { module in
            await module.register()
        }
    }
}

Create Module Extensions

swift
extension RegisterModule {
    var exchangeUseCaseModule: @Sendable () -> Module {
        makeUseCaseWithRepository(
            ExchangeRateInterface.self,
            repositoryProtocol: ExchangeRateInterface.self,
            repositoryFallback: MockExchangeRepositoryImpl(),
            factory: { repo in
                ExchangeUseCaseImpl(repository: repo)
            }
        )
    }

    var exchangeRepositoryModule: @Sendable () -> Module {
        makeDependency(ExchangeRateInterface.self) {
            ExchangeRepositoryImpl()
        }
    }
}

Benefits

1. Less Boilerplate

  • Before: Manual registration calls in every app
  • After: Framework handles registration automatically

2. Cleaner App Initialization

swift
// Clean and simple app initialization
@main
struct CurrencyConverterApp: App {
    init() {
        WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
            await AppDIManager.shared.registerDefaultDependencies()
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

3. Better Organization

Organize dependencies by feature using module extensions:

swift
// Authentication Module
extension RegisterModule {
    var authRepositoryModule: @Sendable () -> Module { ... }
    var authUseCaseModule: @Sendable () -> Module { ... }
}

// User Module
extension RegisterModule {
    var userRepositoryModule: @Sendable () -> Module { ... }
    var userUseCaseModule: @Sendable () -> Module { ... }
}

// Currency Module
extension RegisterModule {
    var exchangeRepositoryModule: @Sendable () -> Module { ... }
    var exchangeUseCaseModule: @Sendable () -> Module { ... }
}

Migration Guide

Step 1: Remove Manual Calls

Remove explicit registerRepositories() and registerUseCases() calls from your app initialization:

swift
// ❌ Remove these lines
await WeaveDI.Container.registerRepositories()
await WeaveDI.Container.registerUseCases()

Step 2: Verify Extensions Exist

Ensure your WeaveDI.Container extensions override the default implementations:

swift
extension WeaveDI.Container {
    static func registerRepositories() async {
        // Your repository registration logic
    }

    static func registerUseCases() async {
        // Your useCase registration logic
    }
}

Step 3: Test Your App

Use bootstrapInTask with AppDIManager to register dependencies:

swift
@main
struct MyApp: App {
    init() {
        WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
            await AppDIManager.shared.registerDefaultDependencies()
        }
    }
}

Advanced: asyncForEach

Use asyncForEach for parallel module registration:

swift
static func registerRepositories() async {
    let repositories = [
        helper.exchangeRepositoryModule(),
        helper.userRepositoryModule(),
        helper.authRepositoryModule(),
    ]

    // Registers all modules in parallel
    await repositories.asyncForEach { module in
        await module.register()
    }
}

Real-World Example

swift
// AutoDIRegistry.swift
import WeaveDI

extension WeaveDI.Container {
    private static let helper = RegisterModule()

    static func registerRepositories() async {
        let repositories = [
            helper.exchangeRepositoryModule(),
        ]

        await repositories.asyncForEach { module in
            await module.register()
        }
    }

    static func registerUseCases() async {
        let useCases = [
            helper.exchangeUseCaseModule(),
        ]

        await useCases.asyncForEach { module in
            await module.register()
        }
    }
}

extension RegisterModule {
    var exchangeUseCaseModule: @Sendable () -> Module {
        makeUseCaseWithRepository(
            ExchangeRateInterface.self,
            repositoryProtocol: ExchangeRateInterface.self,
            repositoryFallback: MockExchangeRepositoryImpl(),
            factory: { repo in
                ExchangeUseCaseImpl(repository: repo)
            }
        )
    }

    var exchangeRepositoryModule: @Sendable () -> Module {
        makeDependency(ExchangeRateInterface.self) {
            ExchangeRepositoryImpl()
        }
    }
}

// App.swift
@main
struct CurrencyConverterApp: App {
    init() {
        WeaveDI.Container.bootstrapInTask { @DIContainerActor _ in
            await AppDIManager.shared.registerDefaultDependencies()
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Best Practices

1. Organize by Feature

Group related dependencies into feature-based modules:

swift
// Feature: Authentication
extension RegisterModule {
    var authModule: @Sendable () -> [Module] {
        [
            authRepositoryModule(),
            authUseCaseModule(),
        ]
    }
}

2. Use Descriptive Names

swift
// ✅ Good - clear and descriptive
var exchangeRateRepositoryModule: @Sendable () -> Module { ... }
var userAuthenticationUseCaseModule: @Sendable () -> Module { ... }

// ❌ Avoid - unclear names
var repo1Module: @Sendable () -> Module { ... }
var module2: @Sendable () -> Module { ... }

3. Document Dependencies

swift
extension RegisterModule {
    /// Exchange rate repository module
    /// Provides currency exchange rate data access
    var exchangeRepositoryModule: @Sendable () -> Module {
        makeDependency(ExchangeRateInterface.self) {
            ExchangeRepositoryImpl()
        }
    }
}

Troubleshooting

Dependencies Not Registered

If dependencies aren't being registered automatically:

  1. Verify you have registerRepositories() and registerUseCases() extensions
  2. Check that bootstrap is being called
  3. Ensure your extensions are in the same target as your app

Debug Logging

Enable debug logging to see registration progress:

swift
#if DEBUG
extension WeaveDI.Container {
    static func registerRepositories() async {
        print("📦 Registering repositories...")
        // ... registration logic
        print("✅ Repositories registered")
    }
}
#endif

See Also

Released under the MIT License.