Software Design

Constructor vs. Property vs. Method Injection: Swift DI

When building iOS apps with Swift, you inevitably face this question.

4 min read
Cover image for Constructor vs. Property vs. Method Injection: Swift DI

When building iOS apps with Swift, you inevitably face this question.

“Which dependency injection approach should I use?”

Let me give you the conclusion first.

Unless you have a specific reason, constructor (init) injection is the closest thing to the right answer.

I’ll explain why, and when to use the other two approaches, based on real-world experience.


Three Injection Approaches: The Essentials First

Before things get confusing, let’s establish the basics. Swift has three main ways to provide dependencies.

  1. Constructor (init) injectioninit receive dependencies as parameters
  2. Property injectionvar assign them to properties later
  3. Method injection — inject them through a separate method

Here is the code for the most recommended approach: constructor injection.

final class OrderService {
    private let repo: MemberRepository // let guarantees immutability

    init(repo: MemberRepository) { // constructor(init) injection
        self.repo = repo
    }
}

It uses only standard Swift syntax, with no extra library, so the code stays clean.

Property injection, by contrast, is this short.

final class OrderService {
    var repo: MemberRepository! // It looks convenient because it’s one line…
}

Property injection clearly looks simpler at first glance. But that convenience can cause problems later.


Why Is Property Injection Discouraged?

Let me start with something I experienced firsthand.

Property injection is genuinely inconvenient in tests. If you forget to inject after creating the object, you get a forced-unwrap crash, and the type alone does not tell you what must be provided to complete it.

With constructor injection, you can insert a mock object directly as OrderService(repo: mockRepo) using plain Swift code.

The second issue is that you cannot use the let keyword.

Constructor injection lets you declare properties as let, making them immutable once injected.

Property injection, on the other hand, uses var, so values can be changed at any time, leaving room for mistakes.

The third issue is circular references.

If A references B and B references A, property injection may not reveal it until the app is running. The failure occurs only when you enter that screen.

Constructor injection exposes the problem when the object is created (and if the design is tangled, it may not compile at all), so you can catch it much earlier.

Circular references fail at different points in time
Circular references fail at different points in time
Testing was painful when I used only field injection
Testing was painful when I used only field injection

Comparison at a Glance

Words can be confusing, so here is a table. (Swift community guidance as of 2026)

Category Constructor (init) injection Property injection Method injection
Immutability (let) Supported ⭕ Not supported ❌ Not supported ❌
Test convenience High Low Medium
Circular reference detection Immediately at creation Found late Found late
Required/optional dependency Best for required dependencies Ambiguous Best for optional dependencies
Code conciseness Medium Very concise Medium

The overall pattern is clear from the table, right?

Constructor injection leads in most categories. That is why DI library documentation, including Swinject and Factory, recommends it as the default.


So When Should You Use the Other Two Approaches?

This does not mean you should always use constructor injection. Each approach has its place.

Method injection works well for optional dependencies.

When an injected target is not required—something you use if available but can live without—you can add it flexibly through a method such as configure(with:).

Property injection still has a place when you cannot control initialization directly, such as with a storyboard-created view controller.

In ordinary application code, it is best to avoid it whenever possible.

To summarize:

  • Required dependency → constructor injection
  • Optional dependency → method injection
  • Property injection in general code → avoid

Frequently Asked Questions

Q. Do DI libraries such as Factory make constructor injection easier?

Yes. If you register how to create dependencies in Swinject or Factory, the container creates the objects to pass to the constructor for you. The code gets shorter while preserving benefits such as let immutability.

Q. What if the constructor ends up with too many parameters?

That is not a problem with the injection approach; it signals that the class is doing too much. First consider splitting responsibilities across separate classes.

Q. Is a DI library necessary even for a small project?

No. For a project with only a few screens, plain constructor injection, passing dependencies directly init, is enough.

When in doubt, this order makes the choice easier
When in doubt, this order makes the choice easier

Property injection’s convenience is tempting at first, but once you write tests and collaborate, you experience firsthand why constructor injection’s benefits are emphasized so strongly.

If you are unsure, start with constructor injection. You will be glad you did as the codebase grows.