swift

Dependency injection explained

Why do we need to inject dependencies and some tools that help with it...

Just like any fake science, computer science has plenty of concepts with complicated names that in reality are very simple. Dependency injection is just one of them. If we have class A that uses (depends on) class B here are two ways we implement it:

// 1
class B {
    private let a = A()

    func callA() -> Bool {
      a.runAVeryLongNetworkingCall()
    }
}

// 2
class B {
    private let a:A

    init(a: A) {
        self.a = a
    }

    func callA() -> Bool {
      a.runAVeryLongNetworkingCall()
    }
}

Obviously 1 is much shorter than 2 so why do we even need 2? Because in 2, by passing the class in the constructor, we do what is called a dependency injection and it does have some advantages especially when it comes to testing. Let’s explore them.

protocol AP {
    func runAVeryLongNetworkingCall() -> Bool
}

class A: AP {
    func runAVeryLongNetworkingCall() -> Bool {
        defaultSession.dataTask(with: url) // ...
        return true
    }
}

class AMock: AP {
    func doAVeryLongNetworkingCall() -> Bool {
        simulateANetworkingCall()
        return true
    }
}

If we were not able to inject a different implementation of A (AMock) then when testing callA function in B we would have to wait for a very long networking call to complete. That of course is not desirable since there is no need to test networking when testing B’s functionality. In short, for testing we can use the mocked version of class A.

func testUsingDependencyInjection() {
    let b = B(a: AMock())
    let result = b.callA()
    wait()
    XCTAssertTrue(result)
}

So being able to inject dependencies is beneficial for testing.

What if we have 30 dependencies? It is not always a good idea to inject all the dependencies through the constructor. In real life we use frameworks like Swinject.

Swinject is just a container (an array or some other data structure) that contains all our dependencies so that, for example, we can pass the container in the constructor instead of all the dependencies.

Subscribe to The infinite monkey theorem

Get the latest posts delivered right to your inbox