Dependency injection for iOS with constructor and property wrapper support.
ForgeInject is a macro-based dependency injection library for iOS. It gives you three ways to resolve dependencies β one is probably the right fit for every scenario you'll encounter.
| Macro | Use when | Resolution |
|---|---|---|
@Injectable |
View models and services with stable dependencies. Constructor injection, swappable in tests. | At init time |
@Inject |
Heavy or optional properties that may never be touched. Lazy + cached + thread-safe. | On first access |
#inject() |
Local variables, default parameters, let in structs. |
Where used |
- Three macros for three injection patterns β pick whichever suits each dependency
- Testable by design β
@Injectablegenerates an init that accepts overrides, so tests don't need to touch the container - Thread-safe β
@Inject's lazy form is backed byMutex<T?>from the Synchronization framework - Three retain policies β
.transient,.singleton,.weak - Modular registration via
ForgeRegisterProtocolfor feature-based organization - Explicit error handling via
ForgeContainerError
- iOS 18+
- macOS 15+
- Swift 6.3+ (Xcode 26 or later)
- File β Add Package Dependenciesβ¦
- Paste
https://github.com/stefanprojchev/ForgeInject.git - Set rule to Up to Next Major from
1.0.0
dependencies: [
.package(url: "https://github.com/stefanprojchev/ForgeInject.git", from: "1.0.0")
],
targets: [
.target(
name: "YourApp",
dependencies: ["ForgeInject"]
)
]import ForgeInject
struct AppDependencies: ForgeRegisterProtocol {
func registerDependencies(in container: ForgeContainerProtocol) {
container.register(with: .singleton) { _ in
NetworkService() as NetworkServiceProtocol
}
container.register(with: .singleton) { _ in
UserRepository() as UserRepositoryProtocol
}
}
}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() }
}
}import ForgeInject
@Injectable
@Observable
final class ProfileViewModel {
let network: NetworkServiceProtocol
let repository: UserRepositoryProtocol
var user: User?
func loadProfile() async {
user = try? await repository.fetchCurrentUser()
}
}
// Production β zero-arg init resolves from the container
let vm = ProfileViewModel()
// Tests β pass mocks directly, no container touching
let vm = ProfileViewModel(
network: MockNetworkService(),
repository: MockUserRepository()
)@Injectable β start here. Generates a matching init with default values resolved from ForgeContainer.shared. Tests override dependencies via normal init arguments β no container manipulation required.
@Injectable
final class ImageProcessor {
let filter: FilterService
let storage: StorageService
}@Inject β lazy, cached, thread-safe. Use for heavy dependencies, circular dependency breaks, or late-bootstrapped services.
final class AdvancedViewModel {
@Inject var heavyService: HeavyService // lazy (default)
@Inject(lazy: false) var logger: LoggerService // eager, no Mutex overhead
}#inject() β freestanding, works anywhere a value is expected. Local variables, default parameters, lazy var initializers.
struct RequestHandler {
let database: Database = #inject()
func handle() async {
let logger: Logger = #inject()
logger.info("handling request")
}
}See the Macros documentation for when to use which.
// Singleton β shared for the container's lifetime
container.register(with: .singleton) { _ in
DatabaseService() as DatabaseServiceProtocol
}
// Transient β new instance every resolve
container.register(with: .transient) { _ in
FormValidator()
}
// Weak β shared while held, recreated after release
container.register(with: .weak) { _ in
ImageCache() as ImageCacheProtocol
}- Getting Started β install, register, resolve
- Macros β
@Injectable,@Inject,#inject()in depth - Registration β protocols, nested deps, retain policies, error handling
- Modular Registration β feature-based organization
- Testing β mock strategies with Swift Testing and XCTest
ForgeInject is part of the Forge family of Swift packages for iOS.
| Package | Description |
|---|---|
| ForgeCore | Thread-safe primitives for iOS Swift packages. |
| ForgeInject | Dependency injection with constructor and property wrapper support. |
| ForgeObservers | Reactive system observers β connectivity, lifecycle, keyboard, and more. |
| ForgeStorage | Type-safe key-value, file, and Keychain storage. |
| ForgeOrchestrator | Orchestrate app flows β startup gates, data pipelines, and continuous monitors. |
| ForgePush | Push notification management β permissions, tokens, and routing. |
| ForgeLocation | Location triggers β geofencing, significant changes, and visits. |
| ForgeBackgroundTasks | Background task scheduling and dispatch. |
ForgeInject is released under the MIT License. See LICENSE.