A tiny, dependency-free Dependency Injection (DI) container for Swift.
Find a file
2026-02-16 13:47:40 +01:00
.github/workflows Add SwiftFormat Action 2026-01-14 12:38:36 -03:00
Sources/MicroContainer Add MIT license to source code 2026-02-16 13:46:27 +01:00
Tests/MicroContainerTests Add MIT license to source code 2026-02-16 13:46:27 +01:00
.gitignore Initial Commit 2022-05-27 18:43:46 +02:00
.swiftformat Add MIT license to source code 2026-02-16 13:46:27 +01:00
.swiftlint.yml Add SwiftLint rules 2026-01-14 11:54:56 -03:00
LICENSE Update documentation 2025-08-25 14:07:34 +02:00
Package.swift Add DependencyBuilder 2025-10-22 13:20:06 +02:00
README.md Update README 2025-08-25 16:18:11 +02:00

MicroContainer

codecov Check Runs Mastodon Follow

A tiny, dependency-free Dependency Injection (DI) container for Swift.

  • Minimal API surface
  • Singleton (static) and factory (dynamic) lifetimes
  • Factories receive the container for nested resolution
  • Thread-safe registration and resolution
  • Optional qualifiers (named registrations)
  • Safer resolve variants and circular dependency detection

Installation

Add MicroContainer to your Package.swift dependencies:

dependencies: [
    .package(url: "https://github.com/otaviocc/MicroContainer.git", from: "0.0.2")
]

Quick Start

let container = DependencyContainer()

// Register a singleton (cached on first resolve)
container.registerSingleton(ServiceProtocol.self) { _ in
    Service()
}

// Register a factory (new instance each time)
container.registerFactory(Repository.self) { _ in
    Repository()
}

// Resolve
let service: ServiceProtocol = container.resolve()
let repo: Repository = container.resolve()

Qualifiers (named registrations)

Register multiple implementations under the same type by using qualifier:

container.registerSingleton(Client.self, qualifier: "primary") { _ in
    Client(baseURL: URL(string: "https://api.example.com")!)
}
container.registerSingleton(Client.self, qualifier: "staging") { _ in
    Client(baseURL: URL(string: "https://staging.example.com")!)
}

let primary: Client = container.resolve(qualifier: "primary")
let staging: Client = container.resolve(qualifier: "staging")

Safer resolve variants

let maybeService: Service? = container.resolveOptional()

do {
    let service: Service = try container.resolveOrThrow()
} catch DependencyContainer.ResolutionError.notRegistered(let type) {
    print("Missing registration: \(type)")
}

Utilities

// Presence
container.contains(Service.self)

// Remove
container.unregister(Service.self)

// Reset all registrations and caches
container.reset()

// Warm up all singletons (instantiate eagerly)
container.warmSingletons()

Circular dependency detection

If a factory resolves a type that forms a cycle, MicroContainer detects it:

  • resolve() fatals with a readable dependency chain
  • resolveOrThrow() throws ResolutionError.circularChain(chain:)

Thread-safety

All registration and resolution operations are thread-safe. Singleton creation is atomic.

License

MIT. See LICENSE.