Introducing InnoDI: macro-based DI with build-time graph validation

Hi everyone,

I’d like to share InnoDI, a macro-based dependency injection library for Swift.

InnoDI is built around an explicit construction graph: you declare inputs and providers in Swift container structs, and macros generate the initializers and typed overrides. The same declarations feed local diagnostics, build-time graph validation, and graph inspection.

Here’s a small example with a live configuration and a test override, after adding the package and InnoDIDAGValidationPlugin to the target:

import InnoDI

struct APIClient { let baseURL: String }

@DIContainer
struct AppContainer {
    @Input var baseURL: String
    @Provide(.shared, APIClient.self, with: [\Self.baseURL])
    var apiClient: APIClient
}

let live = AppContainer(baseURL: "https://api.example.com")
let test = AppContainer(baseURL: "https://api.example.com") {
    $0.apiClient = APIClient(baseURL: "https://test.example.com")
}
precondition(live.apiClient.baseURL == "https://api.example.com")
precondition(test.apiClient.baseURL == "https://test.example.com")

Here, baseURL becomes an initializer parameter. The test container replaces apiClient through the generated override API.

For context, Factory offers typed factories and flexible resolution, swift-dependencies provides environment-style dependencies and scoped overrides, and Needle combines hierarchical components with a code generator. InnoDI’s approach is to generate the container API with attached macros and pair it with a required build-time validator.

That combination gives you:

  • One container declaration for inputs and providers, with generated initializers and typed override APIs for callers
  • Local macro diagnostics plus cross-file initializer checks, hierarchy validation, and global dependency-graph checks in the build plugin
  • Parent–child composition through @SubContainer, graph inspection through a CLI, and SwiftUI app/feature-root helpers
  • An opt-in owned-container API for preparing, retrying, and closing generated async shared-provider scopes

The tradeoff is the plugin setup and the need to keep container declarations within the supported shapes. This is intended for apps where reviewing construction, ownership, and graph changes is worth that extra integration step.

For AI-assisted development, the repository also includes a dedicated InnoDI agent skill. It provides version-aware guidance for implementation, testing, diagnostics, and migration, with separate installation instructions for Codex and Claude Code.

InnoDI is MIT-licensed. The current release is 7.0.1, a maintenance update to 7.0.0. It uses Swift tools 6.2 and supports Apple platforms: iOS 17+, macOS 14+, watchOS 10+, tvOS 17+, and visionOS 1+. The README covers compatibility and the explicit container-isolation setting needed for targets with default MainActor isolation.

Feedback on the API and the balance between explicit wiring and setup effort would be welcome.

GitHub · README and setup · 7.0.1 release notes