Swinject: what it is, Dependency Injection principles and how it works

Author: IT Sectr Published: 2026-05-04 Reading time: 8 min

Swinject is a DI container for Swift that implements the Dependency Injection pattern in iOS applications. The framework automates the creation and injection of dependencies, eliminating manual object and factory management. According to Swinject on GitHub, the library supports Constructor Injection, Property Injection and Method Injection with a flexible scopes system for managing lifetimes.

Key Takeaways

  • Swinject — a DI container for Swift that automates dependency injection in iOS projects.
  • Dependency Injection — a pattern where an object receives its dependencies from the outside rather than creating them internally.
  • Container — the central component of Swinject that stores a registry of registered services and their factories.
  • Service — an abstraction in the form of a protocol for which the container stores a concrete implementation.
  • ObjectScope — a mechanism that determines the instance lifetime: graph, container or transient.

What is Swinject and Dependency Injection

Swinject is an open-source DI container for the Swift language, designed to simplify dependency injection in applications for iOS, macOS and watchOS. The framework uses the Service Locator approach: services are registered in a central container, and the container automatically resolves the dependency graph when an instance is requested.

Dependency Injection (DI) is a design pattern where an object receives its dependencies from the outside rather than creating them internally. This reduces coupling between components, simplifies unit testing and allows replacing implementations without modifying consumer code.

According to Martin Fowler (2004), DI is a specific case of Inversion of Control and is implemented through constructor, property or method injection. Swinject automates this process, eliminating the need to manually write factories and service locators.

Use Swinject in projects with three or more services that have cross-dependencies, where manual object construction leads to initialization code bloat and reduced testability.

Swinject integrates tightly with the Apple ecosystem and supports all Swift versions starting from 3.0. The framework is compatible with Objective-C through bridges, allowing it to be introduced into existing mixed-language projects without a full code migration. This is especially relevant for large applications with a development history of more than five years.

How the Swinject container works

The Swinject container is implemented by the Container class, which stores a registry of registered services. When the resolve method is called, the container creates an object, resolving all its dependencies recursively through the registration graph.

Container and Service

Container is the central object where mappings between abstractions and their implementations are registered. A Service is a protocol that defines a contract, while a Component is a class that implements this protocol. Registration is done using the register method, which takes the service type and a factory.

swift
let container = Container()
container.register(Networking.self) { _ in
    NetworkService()
}
let service = container.resolve(Networking.self)

The resolve method returns an instance of the concrete implementation registered for the specified protocol. If a dependency is not registered, the container throws a fatal error for quick problem detection during development.

Registration and named services

Each registration creates an entry with a factory function and a selected scope. A single service can have multiple registrations with different names, allowing you to select a specific implementation by name — useful for different environments (development, staging, production).

The dependency resolution process works recursively: when the container creates a Component instance, it analyzes its initializer and for each parameter calls resolve with the corresponding type. If a dependency also has its own dependencies, the process continues until the entire graph is fully built. The nesting depth is limited only by available memory, but in practice rarely exceeds five levels.

Dependency injection methods in Swinject

Swinject supports three main dependency injection methods, each applicable depending on the architectural context.

Constructor Injection

Constructor Injection injects dependencies through initializer parameters. This is the preferred method, ensuring that an object is always in a valid state from the moment of creation. Swinject automatically resolves all dependencies passed to the constructor.

swift
class LoginViewModel {
    private let authService: AuthProtocol

    init(authService: AuthProtocol) {
        self.authService = authService
    }
}

container.register(AuthProtocol.self) { _ in
    AuthService()
}
container.register(LoginViewModel.self) { r in
    LoginViewModel(authService: r.resolve(AuthProtocol.self)!)
}

Property Injection

Property Injection injects dependencies by setting object properties after initialization. It is used when a dependency is optional or cannot be passed through the constructor, for example, when working with Storyboard, where the view controller is created automatically. Swinject supports the @Inject annotation for automatic property injection at runtime without an explicit resolve call.

When using Property Injection, it is important to ensure that the dependency is set before the first access to the object. Otherwise, the property will remain nil, leading to an unexpected crash. Swinject solves this problem through the Implicitly Unwrapped Optional mechanism and strict validation at the dependency graph resolution stage.

Method Injection

Method Injection injects dependencies through method parameters. It is used for services that are only needed to perform a single operation and should not be stored as permanent object state. This is the least common but useful for callbacks injection method.

Scopes in Swinject and their purpose

ObjectScope is a mechanism that determines the lifetime of a created instance inside the Swinject container. The framework provides three built-in scopes with the ability to create custom ones through the ObjectScopeProtocol.

ObjectScope.graph

The graph scope is the default value. Each resolve call creates a new instance that lives only for the duration of the dependency graph resolution. This is a safe choice for stateless services as it eliminates memory leaks from caching.

ObjectScope.container

The container scope is a singleton within the container. The instance is created once on the first resolve and returned for all subsequent requests. Suitable for services with shared state: data cache, logger, application settings.

ObjectScope.transient

The transient scope creates a new instance on every resolve call without caching. Used for lightweight objects that do not need to be reused — for example, modules handling a specific HTTP request.

ScopeLifetimeRecommended use
graphFor the duration of graph resolutionStateless services by default
containerLifetime of the containerSingletons: cache, logger, network client
transientNo cachingLightweight objects for single use

Swinject in iOS projects

Integrating Swinject into a real iOS project starts with initializing the container at application launch — in AppDelegate or the scene. It is recommended to structure registrations using Assembly: a separate class or struct that groups related services.

According to a Swift Developer Community survey (2025), 43% of iOS developers use DI containers in commercial projects to manage dependencies for the network layer, repositories, and navigation coordinators. Swinject remains the most popular solution due to its minimal syntax and Objective-C compatibility.

Storyboard Injection is a unique Swinject feature: the container automatically injects dependencies into view controllers created from Storyboard without additional code in AppDelegate. This uses a special resolver passed to UIStoryboard via the init(container:) method, which intercepts view controller creation and injects registered dependencies.

In large projects, Swinject can be combined with navigation coordinators: the coordinator receives the container and creates screens by resolving their dependencies through resolve, maintaining a single configuration point for the entire scene.

Assembly architecture is the recommended pattern for organizing registrations. Each Assembly groups related services (e.g., NetworkingAssembly, DatabaseAssembly) and can depend on other Assemblies. When initializing the container, all Assemblies are loaded and register their services, providing clear separation of concerns and simplifying navigation through the DI configuration in large projects with dozens of services.

For debugging the DI graph, Swinject provides the SwinjectPropertyLoader extension, which loads configuration from a plist file, and SwinjectStoryboard — storyboard integration through a special version of UIStoryboard. These tools are especially useful when migrating an existing project from manual object construction to DI: the developer can gradually register services, checking the dependency graph through tests and resolution error logging without stopping main feature development.

Swinject also provides integration with RxSwift and Combine through the SwinjectAutoregistration extension for automatic dependency resolution based on initializer parameter types without explicit factory registration. This reduces the amount of registration code for simple services: just call container.register(ServiceProtocol.self) without specifying a factory, and Swinject will automatically build the factory based on Signal reflection provided by the Swift runtime. This approach is recommended for services whose constructors only accept basic types and do not require complex creation logic.

Frequently Asked Questions

How is Swinject different from other DI frameworks for Swift?

Swinject is written in pure Swift without code generation or reflection. Unlike Needle, it does not require source generation, and compared to Dip, it provides built-in Storyboard Injection support, simplifying integration into existing UIKit projects.

How to install Swinject via Swift Package Manager?

Add the package via URL github.com/Swinject/Swinject through Xcode in the File — Add Packages menu. Installation via CocoaPods and Carthage is also available. After installation, import the Swinject module and create a Container instance.

Can Swinject be used in SwiftUI projects?

Yes, Swinject is fully compatible with SwiftUI. Dependencies are injected through View initializers or through Environment, where the container is passed as an EnvironmentObject. Swinject does not depend on UIKit and works equally well with both frameworks.

How to use Swinject for unit testing?

Create a separate container for tests, replacing real services with mocks. Swinject allows overriding registrations without changing consumer code. Each test gets an isolated container with a minimal set of dependencies.

Which scope should I choose for an analytics service?

For analytics, use the container scope so that all screens send events through a single instance. This guarantees a unified send queue and correct batch aggregation operation without data duplication between different consumers.

Summary

  • Swinject — a DI container for Swift that automates dependency injection through Container and ObjectScope.
  • Dependency Injection reduces code coupling, simplifies testing, and allows replacing implementations without changing consumers.
  • Container — a service registry supporting register for registration and resolve for instance retrieval.
  • Constructor Injection is the preferred injection method, ensuring the object’s valid state.
  • ObjectScope manages lifetime: graph (default), container (singleton) and transient (no cache).
  • Storyboard Injection automatically injects dependencies into UIKit scenes without manual setup.
  • For unit tests, use a separate container with mock service implementations.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also