This is a lightweight, flexible, and production-ready Dependency Injection (DI) system for Swift and SwiftUI apps, inspired by modern patterns from Clean Architecture and Domain-Driven Design. It supports both type-based and key-path-based registrations and resolutions, making it ideal for modular, scalable, and testable applications.
- ✅ Type-safe dependency resolution
- ✅ KeyPath-based scoped dependency access
- ✅ Property wrappers for SwiftUI and non-SwiftUI types
- ✅ Clean API for registration and usage
- ✅ Seamless integration with
@StateObject
and@ObservedObject
- ✅ Support for both struct-based and class-based models
Use Swift Package Manager:
// swift-tools-version:6.1
import PackageDescription
let package = Package(
name: "YourPackageName",
dependencies: [
.package(url: "https://github.com/Stof83/DependencyKit.git", from: "1.0.0")
],
targets: [
.target(
name: "YourTargetName",
dependencies: [
.product(name: "DependencyKit", package: "DependencyKit")
]
)
]
)
class ViewModel1: ObservableObject {}
class ViewModel2: NSObject {}
You can register dependencies by type or by key path.
Extend DependencyValues
to define scoped keys:
extension DependencyValues {
var baseURL: URL {
get { resolve(\.baseURL) }
set { register(newValue, for: \.baseURL) }
}
}
Register the dependency:
let baseURL = URL(string: "https://api.example.com")!
let dependencyManager = DependencyManager()
dependencyManager.register(baseURL, for: \.baseURL)
let viewModel1 = ViewModel1()
let viewModel2 = ViewModel2()
let dependencyManager = DependencyManager()
dependencyManager.register(dependencies: [
(viewModel1, ViewModel1.self),
(viewModel2, ViewModel2.self)
])
struct MyView: View {
@InjectedStateObject var viewModel1: ViewModel1
@Dependency(\.baseURL) var baseURL: URL
var body: some View {
VStack {
Text("Base URL: \(baseURL.absoluteString)")
Text("ViewModel1 class: \(String(describing: type(of: viewModel1)))")
}
}
}
class Service {
@InjectedViewModel var viewModel2: ViewModel2
func printType() {
print("Injected ViewModel2 type: \(type(of: viewModel2))")
}
}
// Replace baseURL at runtime
dependencyManager.register(URL(string: "https://new.api.com")!, for: \.baseURL)
// Remove a dependency by type
dependencyManager.remove(ViewModel1.self)
- Register mock implementations for unit tests.
- Use a custom
DependencyContainer
instance for isolation.
let testContainer = DependencyContainer()
testContainer.register(MockService() as ServiceProtocol, for: ServiceProtocol.self)
DependencyKit is released under the MIT license. See LICENSE for details.