Software Design

DI, IoC, and DIP: a complete guide to the differences

DI, IoC, and DIP are concepts at different levels. DIP is the principle that determines what to depend on, IoC is about who holds control, and DI is the technique of supplying dependencies. This guide covers the distinctions with examples and interview answers.

5 min read
Cover image for DI, IoC, and DIP: a complete guide to the differences

If you study software development, you have probably been confused by DI, IoC, and DIP at least once.

When courses and articles say things like “an IoC container injects dependencies through DI to follow DIP,” it is easy to lose track of what each term means.

Here is the conclusion first: the three terms operate at different levels. DIP is the principle—why things should be done that way. IoC is the broad direction that realizes the principle—who has control. DI is the concrete technique—how dependencies are supplied.

After reading this article, you should understand how the three concepts connect and be able to answer interview questions about them without hesitation. I have kept the code examples as short as possible.

A one-line summary of the three terms

It is much easier if you memorize one line for each term first.

  • DIP (Dependency Inversion Principle): a design principle that says to depend on abstractions, such as protocols, rather than concrete implementations.
  • IoC (Inversion of Control): a structure in which the framework or container, rather than your code, controls the program’s flow.
  • DI (Dependency Injection): a technique that supplies required objects from outside instead of creating them internally.

The relationship among the three looks like this.

DIP is the goal and principle, IoC is the broader concept used to achieve it, and DI is one concrete way to implement IoC.

In short, the level of abstraction decreases in this order: DIP > IoC > DI.

A hierarchy diagram branching from DIP through IoC to DI, callbacks, and template methods
The levels occupied by the three terms, from principle to technique

DIP is about what to depend on

DIP is the D in SOLID, the Dependency Inversion Principle among the five core object-oriented design principles.

The key is twofold: high-level modules should not depend on low-level modules, and both should depend on abstractions.

That sounds abstract, but it becomes much easier with code.

Consider a bad example in which an order service is directly tied to a specific payment provider, Kakao Pay.

// Bad example: directly depending on a concrete class
class OrderService {
    private let pay = KakaoPay() // Changing the payment provider requires tearing this out
}

To switch to Naver Pay later, you would have to modify the OrderService code directly. Every new payment method would shake up the high-level module.

DIP reverses this. You define an abstraction—a payment protocol—and depend only on it.

protocol PayGateway { func pay(amount: Int) }

class OrderService {
    private let pay: PayGateway // Depend on an abstraction, not a concrete class
    init(pay: PayGateway) { self.pay = pay }
}

Whether it is Kakao Pay or Naver Pay, you only need to implement PayGateway; OrderService does not need to change. That is what it means for the dependency direction to be inverted.


IoC is about who has control

IoC, or Inversion of Control, is a somewhat broader concept.

Normally, the code we write controls the entire flow: we create objects, call methods, and determine the order of execution.

IoC reverses that control. Instead of us calling the framework, the framework calls us.

It is often called the Hollywood Principle: “Don’t call us, we’ll call you.”

With a DI container such as Swinject, the container creates, manages, and wires objects instead of you creating them directly. Control moves from you to the container.

Here is one important point: IoC is not limited to DI.

  • A framework invoking a callback
  • The Template Method pattern
  • UIKit invoking lifecycle methods such as viewDidLoad on your behalf

These are all examples of IoC. DI is only a sub-concept specialized in supplying dependencies.

An old telephone on a desk with a “Don’t call us, we’ll call you” card
IoC is simply the transfer of control

DI is about how dependencies are supplied

DI, or Dependency Injection, is the most representative technique for implementing IoC.

In the earlier DIP example, OrderService received PayGateway through its initializer. That is DI: supplying a required object from outside instead of creating it internally.

There are three main injection methods.

Injection method Description Recommendation
Constructor injection Receive the dependency through init Most recommended (immutable, required dependencies are explicit)
Method injection Receive it as a method parameter For optional dependencies
Property injection Assign it to a property after initialization Not recommended because it becomes optional and var

Swift recommends initializer injection. The dependency can be fixed with let, making it safer, and fake objects (Mocks) are easy to provide in tests.

The relationship can be summarized like this.

To follow the DIP principle, transfer control in the direction of IoC and use DI as the concrete means.

This sentence is the most concise description of the relationship among the three terms.

A summary diagram connecting three boxes labeled WHY, WHO, and HOW with arrows on a whiteboard
Separating the concepts into why, who, and how makes them easy to remember

Frequently asked questions

Q. Aren’t DI and IoC the same thing?

No. IoC is the broader concept, while DI is one of several ways to implement IoC. All DI is IoC, but not all IoC is DI.

Q. Does following DIP automatically mean using DI?

Not necessarily. DIP is the principle “depend on abstractions.” DI begins when an implementation of that abstraction is supplied from outside. The principle and the technique are separate.

Q. Can I use DI without a library such as Swinject?

Yes. Passing a dependency directly through an initializer, as in the Swift example above, is also DI. Swinject and Factory only automate the process; DI itself does not require a library.


To summarize: DIP is why—the principle; IoC is who—the control; and DI is how—the technique. Remembering the three questions this way will keep them clear.

Once you understand that they operate at different levels, DI-related code looks completely different. I hope this article helped you organize the concepts clearly. You’ve got this!

Continue reading