A lightweight dependency injection container for Swift with support for singletons, transients, and property wrappers.
dependencies: [
.package(url: "https://github.com/tikhop/TinyDI.git", from: "1.0.0")
]import TinyDI
// Register a service
DIContainer.register(DatabaseService.self) {
DatabaseService()
}
// Resolve the service
let db = DIContainer.resolve(DatabaseService.self)Instance created once and reused for all resolutions:
DIContainer.singleton(Logger.self) {
ConsoleLogger()
}
let logger1 = DIContainer.resolve(Logger.self)
let logger2 = DIContainer.resolve(Logger.self)
// logger1 and logger2 are the same instanceNew instance created for each resolution:
DIContainer.transient(APIClient.self) {
APIClient()
}
let client1 = DIContainer.resolve(APIClient.self)
let client2 = DIContainer.resolve(APIClient.self)
// client1 and client2 are different instancesRegister multiple implementations of the same type:
DIContainer.register(Database.self, name: "primary") {
PostgreSQL()
}
DIContainer.register(Database.self, name: "cache") {
Redis()
}
let primaryDB = DIContainer.resolve(Database.self, name: "primary")
let cacheDB = DIContainer.resolve(Database.self, name: "cache")Services can depend on other services:
DIContainer.singleton(Logger.self) {
ConsoleLogger()
}
DIContainer.singleton(UserService.self) { container in
UserService(logger: container.resolve(Logger.self))
}Use @Injected for automatic dependency resolution:
class ViewController {
@Injected var userService: UserService
@Injected(name: "primary") var database: Database
func loadUser() {
let user = userService.getCurrentUser()
}
}Conform to Injectable for automatic registration:
class UserRepository: Injectable {
typealias Dependencies = DIContainer
let logger: Logger
let database: Database
required init(dependencies: DIContainer) {
self.logger = dependencies.resolve(Logger.self)
self.database = dependencies.resolve(Database.self)
}
}
// Register with automatic dependency injection
DIContainer.default.register(UserRepository.self, lifetime: .singleton)Both instance and static methods are available:
// Using static methods (operates on default container)
DIContainer.register(Service.self) { Service() }
let service = DIContainer.resolve(Service.self)
// Using instance methods
let container = DIContainer()
container.register(Service.self) { Service() }
let service = container.resolve(Service.self)Registration methods support chaining:
DIContainer.default
.singleton(Logger.self) { ConsoleLogger() }
.singleton(Database.self) { PostgreSQL() }
.transient(APIClient.self) { APIClient() }TinyDI uses NSRecursiveLock internally, making it thread-safe for concurrent access from multiple threads.
register(_:name:lifetime:factory:)- Register a service with specified lifetimesingleton(_:name:factory:)- Register a singleton servicetransient(_:name:factory:)- Register a transient serviceresolve(_:name:)- Resolve a registered serviceclearAll()- Remove all registrations
.singleton- Single instance shared across all resolutions.transient- New instance for each resolution
Property wrapper for automatic dependency injection. Supports named services via @Injected(name:).
- Swift 6.1+
- iOS 13.0+ / tvOS 13.0+
MIT