FI
ForgeInject

Getting Started

Install ForgeInject, register your first dependency, and resolve it with @Injectable.

Requirements

  • iOS 18+ / macOS 15+
  • Swift 6.3+ (Xcode 26 or later)

Installation

Register Dependencies

Create a struct conforming to ForgeRegisterProtocol to hold your registrations. This separates registration logic from your app entry point and scales cleanly as your app grows.

AppDependencies.swift
import ForgeInject

struct AppDependencies: ForgeRegisterProtocol {
    func registerDependencies(in container: ForgeContainerProtocol) {
        container.register(with: .singleton) { _ in
            GreetingService() as GreetingServiceProtocol
        }
    }
}

Initialize the container at app launch and call your registration struct:

Set once: ForgeContainer.shared can only be set once per app launch. Subsequent non-nil assignments trigger a precondition failure.

Consume Dependencies

Apply @Injectable to any class, struct, or actor with let stored properties. The macro generates a matching init where each parameter defaults to ForgeContainer.shared's resolved value.

GreetingViewModel.swift
import ForgeInject

@Injectable
@Observable
final class GreetingViewModel {
    let greetingService: GreetingServiceProtocol

    var message = ""

    func sayHello() {
        message = greetingService.greet(name: "World")
    }
}

Production vs Tests

Because the generated init accepts explicit arguments, tests can swap mocks in without touching the container at all.

// Production — zero-arg init resolves from ForgeContainer.shared
let viewModel = GreetingViewModel()

// Tests — pass a mock directly, no container touching
let viewModel = GreetingViewModel(greetingService: MockGreetingService())

Next: ForgeInject ships three macros — @Injectable, @Inject, and #inject() — each suited to a different scenario. See the Macros page to learn when to use which.