Modular Registration
Organize registrations by feature module as your app grows.
The Protocol
ForgeInject provides ForgeRegisterProtocol for grouping related registrations into self-contained modules. Each module registers only the dependencies it owns.
public protocol ForgeRegisterProtocol {
func registerDependencies(in container: ForgeContainerProtocol)
} Feature Modules
Create one struct per feature area. Each module is responsible for its own dependencies.
import ForgeInject
struct NetworkModule: ForgeRegisterProtocol {
func registerDependencies(in container: ForgeContainerProtocol) {
container.register(with: .singleton) { _ in
URLSession.shared as URLSessionProtocol
}
container.register(with: .singleton) { container in
let session: URLSessionProtocol = try container.resolve()
return NetworkService(session: session) as NetworkServiceProtocol
}
}
} import ForgeInject
struct UserModule: ForgeRegisterProtocol {
func registerDependencies(in container: ForgeContainerProtocol) {
container.register(with: .singleton) { container in
let networkService: NetworkServiceProtocol = try container.resolve()
return UserRepository(networkService: networkService) as UserRepositoryProtocol
}
container.register(with: .transient) { container in
let repository: UserRepositoryProtocol = try container.resolve()
return UserService(repository: repository) as UserServiceProtocol
}
}
} Root Composition
Create a root AppDependencies struct that composes all feature modules. This is the single entry point for all registrations — your app entry point only ever calls this one struct. See the Modular App example for a full implementation including an AuthModule.
import ForgeInject
struct AppDependencies: ForgeRegisterProtocol {
func registerDependencies(in container: ForgeContainerProtocol) {
NetworkModule().registerDependencies(in: container)
AuthModule().registerDependencies(in: container)
UserModule().registerDependencies(in: container)
}
} import SwiftUI
import ForgeInject
@main
struct TaskFlowApp: App {
init() {
let container = ForgeContainer()
AppDependencies().registerDependencies(in: container)
ForgeContainer.shared = container
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
} Note: Registration order does not matter. Dependencies are resolved lazily on first access, not at registration time. However, grouping modules by dependency layer (infrastructure → domain → feature) improves readability.
Consuming Modular Dependencies
Once registered, any @Injectable type automatically picks up the right dependency by its property type — modules are transparent at the consumption site.
Best Practices
- One module per feature area — network, auth, user, tasks, etc. Modules should not know about each other except through the shared container.
- Cross-module dependencies via protocols only — never import a concrete type from another module's internals.
- Group modules by layer in your root composition — infrastructure (network) first, then domain (auth/user), then feature-specific.