The Swift Dependency Injection Container is a lightweight and flexible library designed to facilitate dependency management in Swift applications. It provides a structured and type-safe approach to resolve dependencies throughout your codebase, promoting code reusability, testability, and maintainability.
- Type-safe dependency resolution.
- Lazy instantiation of dependencies.
- Property wrappers for convenient dependency injection.
- Dynamic module registration and management.
- Result builder syntax for declarative module registration.
- Debug-only scanners for discovering injection keys and auto modules.
- Automatic module registration for debug builds via
AutoModuleandContainer.autoRegisterModules().
- Swift 5.9+
DIContainer is available Swift Package Manager.
in Package.swift add the following:
dependencies: [
.package(url: "https://github.com/minsOne/DIContainer.git", from: "1.0.0")
]First, register a key and module pair to a Container, where the module has concreate type. Key has protocol type, and concreate type is inheriting protocol type
Container {
Module(AnimalKey.self) { Cat() }
}
.build()Then get an instance from the container.
@Inject(AnimalKey.self)
var cat: Meow
cat.doSomething() // prints "Meow.."
// or
Container[AnimalKey.self].doSomething() // prints "Meow.."Where definitions of the protocols and struct are
class AnimalKey: InjectionKey {
var type: Meow?
}
protocol Meow {
func doSomething()
}
struct Cat: Meow {
func doSomething() {
print("Meow..")
}
}DIContainer includes debug-only scanners that discover injection keys and auto modules from the loaded app:
ModuleScanneruses the Objective-C runtime to scan classes.MachOLoaderscans Swift metadata from loaded Mach-O images.Container.autoRegisterModules()builds a container by converting scannedAutoModuleimplementation types intoModulevalues.
The key and the implementation have different responsibilities:
- An
InjectionKeydefines the dependency contract. ItsValueis usually a protocol or abstract type that callers depend on. - An
AutoModuleimplementation provides the concrete type. ItsModuleKeyTypetells the scanner which key should be used for registration. keyListis useful for diagnostics, but automatic registration is driven by scannedAutoModuleimplementations and theirModuleKeyType.
In the example below, FooServiceKey exposes FooService as the resolvable type, and FooServiceImpl is the concrete implementation discovered by the scanner.
final class FooServiceKey: InjectionKey {
typealias Value = FooService
}
protocol FooService {
func doSomething()
}
final class FooServiceImpl: AutoModule, FooService {
typealias ModuleKeyType = FooServiceKey
func doSomething() {
print("Foo")
}
}After scanning, DIContainer treats the implementation like this explicit registration:
Module(FooServiceKey.self) {
FooServiceImpl() as FooService
}
// or
Module(FooServiceImpl.self)Then register all scanned modules in a debug composition root.
#if DEBUG
Container.autoRegisterModules()
#endifYou can also inspect scan results directly.
let keys = ModuleScanner().keyList
let modules = ModuleScanner().scanModuleList
Container(modules: modules).build()Scanner APIs are compiled only under #if DEBUG. They are useful for diagnostics and demo/test registration, while production composition should remain explicit and covered by tests.
$ (cd MockData/Sources/MockData && for i in {1..20}; do cp MockClass.swift MockClass$i.swift; done)
$ swift test
$ swift test -Xswiftc -OContributions to the Swift Dependency Injection Container are welcome. Here are ways you can contribute:
- Reporting issues
- Suggesting enhancements
- Submitting pull requests with bug fixes or new features
Please ensure to follow the coding standards and write tests for new functionality.
This project is licensed under the MIT License.
- [Swift 5.7+] Dependency Injection (1) - PropertyWrapper를 이용한 Service Locator 구현하기
- [Swift 5.7+] Dependency Injection (2) - 컨테이너 무결성 보장해 보기
- [Swift 5.7+][Objective-C] Dependency Injection (3) - objc_getClassList를 사용하여 모든 클래스 목록 얻기
The DIContainer are inspired by:
- Dependency Injection in Swift using latest Swift features
- iOS Dependency Injection Using Swinject
- Dependency Injection via Property Wrappers
- DI 라이브러리 “Koin” 은 DI가 맞을까?
- SwiftLee 방식의 DI를 하는 것으로 TCA의 Environment 버킷 릴레이를 그만두고 싶은 이야기
- Swift Dependency Injection via Property Wrapper
- 뱅크샐러드 안드로이드 앱에서 Koin 걷어내고 Hilt로 마이그레이션하기
- 마틴 파울러 - Inversion of Control Containers and the Dependency Injection pattern
- Nest.js는 실제로 어떻게 의존성을 주입해줄까?
- mikeash.com - Friday Q&A 2014-08-08: Swift Name Mangling
- Wikipedia - Name mangling
- Github - Apple/swift-service-context
- Github - baidu/CarbonGraph
- Github - DerekSelander/dsdump
- Github - p-x9/MachOKit
- Github - SpectralDragon/DITranquillity
- Github - ukhsa-collaboration/covid-19-app-ios-ag-public
- Building a class-dump in 2020