What it is
Swinject is a dependency injection framework for Swift, using a container with registered factories, object scopes and assemblies.
Register factories against protocols with an object scope, then resolve. Assemblies group related registrations into modules.
Installation
.package(url: "https://github.com/Swinject/Swinject.git", from: "2.9.1")Getting started
The smallest useful thing you can do with it, and what each part means.
swift
let container = Container()
container.register(APIClient.self) { _ in
URLSessionAPIClient(session: .shared)
}.inObjectScope(.container) // singleton for the container's lifetime
container.register(BookRepository.self) { r in
RemoteBookRepository(api: r.resolve(APIClient.self)!)
}
container.register(BookViewModel.self) { r in
BookViewModel(repository: r.resolve(BookRepository.self)!)
}.inObjectScope(.transient) // a new one each time
let viewModel = container.resolve(BookViewModel.self)!Advanced usage
Where the library earns its place over a simpler alternative.
swift
final class NetworkAssembly: Assembly {
func assemble(container: Container) {
container.register(APIClient.self) { _ in URLSessionAPIClient() }
.inObjectScope(.container)
}
}
final class DataAssembly: Assembly {
func assemble(container: Container) {
container.register(BookRepository.self) { r in
RemoteBookRepository(api: r.resolve(APIClient.self)!)
}
}
}
let assembler = Assembler([NetworkAssembly(), DataAssembly()])
// Tests: register over the real binding.
let testAssembler = Assembler([NetworkAssembly(), DataAssembly()])
testAssembler.container.register(APIClient.self) { _ in MockAPIClient() }Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Unexpectedly found nil when resolving
- The type was never registered, or a different protocol was used. Swinject cannot catch this at compile time — add a resolution smoke test.
- Objects are retained longer than expected
- An overly broad object scope. Use .transient or .graph unless a singleton is genuinely intended.
Best practices
- Group registrations into Assemblies so the composition root stays readable.
- Choose scopes deliberately — .container is a singleton and will retain whatever it holds.
- Resolve only at the composition root; passing the container around is a service locator.
- Add a test that resolves every registered type, since resolution failures are runtime-only.
Background
Why it exists, and what it was reacting to.
Swinject is the long-standing DI container for Swift, predating the property-wrapper approaches. Its assembly concept keeps registration organised in large applications.
