Swift Language Dependency Injection Dependency Injection Types


Example

This example will demonstrate how to use Dependency Injection (DI) design pattern in Swift using these methods:

  1. Initializer Injection (the proper term is Constructor Injection, but since Swift has initializers it's called initializer injection)
  2. Property Injection
  3. Method Injection

Example Setup without DI

    protocol Engine {
        func startEngine()
        func stopEngine()
    }
    
    class TrainEngine: Engine {
        func startEngine() {
            print("Engine started")
        }
        
        func stopEngine() {
            print("Engine stopped")
        }
    }
    
    protocol TrainCar {
    var numberOfSeats: Int { get }
    func attachCar(attach: Bool)
}

class RestaurantCar: TrainCar {
    var numberOfSeats: Int {
        get {
            return 30
        }
    }
    func attachCar(attach: Bool) {
        print("Attach car")
    }
}

class PassengerCar: TrainCar {
    var numberOfSeats: Int {
        get {
            return 50
        }
    }
    func attachCar(attach: Bool) {
        print("Attach car")
    }
}
    
class Train {
    let engine: Engine?
    var mainCar: TrainCar?
}

Initializer Dependency Injection

As the name says, all dependencies are injected through the class initializer. To inject dependencies through the initializer, we'll add the initializer to the Train class.

Train class now looks like this:

class Train {
    let engine: Engine?
    var mainCar: TrainCar?
    
    init(engine: Engine) {
        self.engine = engine
    }
}

When we want to create an instance of the Train class we'll use initializer to inject a specific Engine implementation:

let train = Train(engine: TrainEngine())

NOTE: The main advantage of the initializer injection versus the property injection is that we can set the variable as private variable or even make it a constant with the let keyword (as we did in our example). This way we can make sure that no one can access it or change it.

Properties Dependency Injection

DI using properties is even simpler that using an initializer. Let's inject a PassengerCar dependency to the train object we already created using the properties DI:

train.mainCar = PassengerCar()

That's it. Our train's mainCar is now a PassengerCar instance.

Method Dependency Injection

This type of dependency injection is a little different that the previous two because it won't affect the whole object, but it will only inject a dependency to be used in the scope of one specific method. When a dependency is only used in a single method, it's usually not good to make the whole object dependent on it. Let's add a new method to the Train class:

func reparkCar(trainCar: TrainCar) {
    trainCar.attachCar(attach: true)
    engine?.startEngine()
    engine?.stopEngine()
    trainCar.attachCar(attach: false)
}

Now, if we call the new Train's class method, we'll inject the TrainCar using the method dependency injection.

train.reparkCar(trainCar: RestaurantCar())