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
    }
}
```swift
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.

```swift
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.

FAQ

Why is dependency injection useful in Swift applications?
Injecting dependencies through initializers or properties decouples types from concrete implementations, which makes it much easier to replace real services with mocks or stubs in tests and to evolve your architecture over time.
How can I manage many dependencies without huge initializers?
For small examples you can inject individual dependencies, but in larger apps it is common to introduce a DI container such as Swinject. The container holds registrations for your services and constructs objects on demand so you avoid passing dozens of parameters around.

Welcome to The infinite monkey theorem

Somewhere a monkey just typed Shakespeare in TypeScript. Be the first to read the masterpieces (and the hilarious misfires) landing on the blog.

Subscribe to The infinite monkey theorem

We fling fresh posts—no banana peels attached—straight to your inbox.