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
structAppDependencies:ForgeRegisterProtocol{func registerDependencies(in container:ForgeContainerProtocol){
container.register(with:.singleton){ _ inNetworkService()asNetworkServiceProtocol}
container.register(with:.singleton){ _ inUserRepository()asUserRepositoryProtocol}}}import SwiftUI
import ForgeInject
@mainstructTaskFlowApp:App{init(){letcontainer=ForgeContainer()AppDependencies().registerDependencies(in: container)ForgeContainer.shared = container
}varbody:someScene{WindowGroup{ContentView()}}}import ForgeInject
@Injectable@ObservablefinalclassProfileViewModel{letnetwork:NetworkServiceProtocolletrepository:UserRepositoryProtocolvaruser:User?func loadProfile()async{
user =try?await repository.fetchCurrentUser()}}
// Production — zero-arg init resolves from the container
letvm=ProfileViewModel()
// Tests — pass mocks directly, no container touching
letvm=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.
@InjectablefinalclassImageProcessor{letfilter:FilterServiceletstorage:StorageService}@Inject — lazy, cached, thread-safe. Use for heavy dependencies, circular dependency breaks, or late-bootstrapped services.
finalclassAdvancedViewModel{@InjectvarheavyService:HeavyService // lazy (default)
@Inject(lazy:false)varlogger:LoggerService // eager, no Mutex overhead
}#inject() — freestanding, works anywhere a value is expected. Local variables, default parameters, lazy var initializers.
structRequestHandler{letdatabase:Database= #inject()func handle()async{letlogger:Logger= #inject()
logger.info("handling request")}}See the Macros section above for when to use which.
// Singleton — shared for the container's lifetime
container.register(with:.singleton){ _ inDatabaseService()asDatabaseServiceProtocol}
// Transient — new instance every resolve
container.register(with:.transient){ _ inFormValidator()}
// Weak — shared while held, recreated after release
container.register(with:.weak){ _ inImageCache()asImageCacheProtocol}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. |
| ForgeDB | Type-safe repository pattern and GRDB-backed SQLite persistence. |
| 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. |
| ForgeNetworking | Typed, async/await-first HTTP networking with auth, retry, and background transfers. |
| ForgeLog | Structured logging with pluggable providers and a built-in inspector UI. |
| ForgeAccess | Subscription-aware feature gating with override channels and debug UI. |
ForgeInject is released under the MIT License. See LICENSE.