Understanding Dependency Injection in Swift iOS — Like Building a Car

Nitish Kumar
2 min readAug 7, 2023

Imagine you are building a car, and you need different types of engines like an electric engine and a gasoline engine to power it. Dependency injection is like having the flexibility to use the engine you want when building the car!

Engine Protocol — Our Engine Blueprint

First, let’s create a blueprint for our engines. We’ll define an `Engine` protocol that has a `start()` method:


protocol Engine {
func start()
}

This protocol acts as a blueprint for all engines and ensures that they have a `start()` method.

Building the Engines — Electric and Gasoline Engines

Now, let’s create two types of engines that conform to the `Engine` protocol:


class ElectricEngine: Engine {
func start() {
print(“Electric engine started.”)
}
}
class GasolineEngine: Engine {
func start() {
print(“Gasoline engine started.”)
}
}

We have built an electric engine and a gasoline engine, both of which have their unique implementation of the `start()` method.

The Car Class — Using Dependency Injection

Now comes the exciting part — building our `Car` class using dependency injection! Dependency injection allows us to choose the engine we want when constructing a car.

Constructor Injection — Building a Car with the Engine You Want

We’ll start with the most common type of dependency injection — constructor injection. It’s like choosing the engine when assembling the car:


class Car {
private let engine: Engine

init(engine: Engine) {
self.engine = engine
}

func startCar() {
engine.start()
}
}
//With this setup, we can build a car with any engine we want!
let electricEngine = ElectricEngine()
let gasolineEngine = GasolineEngine()
let electricCar = Car(engine: electricEngine)
let gasolineCar = Car(engine: gasolineEngine)
electricCar.startCar() // Output: “Electric engine started.”
gasolineCar.startCar() // Output: “Gasoline engine started.”

Property Injection — Swapping Engines Easily

Next, we have property injection. It’s like changing the engine of the car after it’s built:


class Car {
var engine: Engine?

func startCar() {
engine?.start()
}
}

Now we can swap engines easily for the same car!


let electricCar = Car()
electricCar.engine = ElectricEngine()
electricCar.startCar() // Output: “Electric engine started.”
let gasolineCar = Car()
gasolineCar.engine = GasolineEngine()
gasolineCar.startCar() // Output: “Gasoline engine started.”

Method Injection — Different Engines for Different Occasions

Lastly, we have method injection. It’s like using a specific engine only when starting the car:


class Car {
func startCar(with engine: Engine) {
engine.start()
}
}

With this method injection, we can use different engines for different cars!


let car = Car()
let electricEngine = ElectricEngine()
let gasolineEngine = GasolineEngine()
car.startCar(with: electricEngine) // Output: “Electric engine started.”
car.startCar(with: gasolineEngine) // Output: “Gasoline engine started.”

Choosing the Right Injection Type

Each type of dependency injection has its use cases.

Constructor injection is generally the most preferred, as it ensures the car has an engine before use.

Property injection is useful when the engine can change during the car’s lifetime.

Method injection allows us to pass different engines for specific situations.

By understanding dependency injection, you have the flexibility to build powerful and flexible apps, just like building different cars with different engines. Happy coding! 🚗💨

--

--

Nitish Kumar

I developed and maintain applications aimed at a range of iOS devices including mobile phones and tablet computers.