What Is Dependency Injection?

What Is Dependency Injection?

Dependency injection (DI for short) at its core is a very simple technique, but using it will enable other advanced patterns and principles that will help you develop clean and loosely-coupled applications.

DI Definitions

Let’s explore some popular definitions of DI.

dependency injection is a technique in which an object receives other objects that it depends on, called dependencies. Wikipedia

In StackOverflow, the popular Q&A website for developers, John Munsch gave an excellent explanation of DI in response to the question “How to explain dependency injection to a 5-year-old?”

When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn’t want you to have. You might even be looking for something we don’t even have or which has expired. What you should be doing is stating a need, “I need something to drink with lunch,” and then we will make sure you have something when you sit down to eat. stackoverflow

Another very compact definition of DI given by James Shore:

Dependency injection means giving an object its instance variables. jamesshore.com

A final definition I will include here is one from the book Dependency Injection Principles, Practices, and Patterns by Steven van Deursen and Mark Seemann. It’s a bit advanced, which states the whole goal of DI:

Dependency Injection is a set of software design principles and patterns that enables you to develop loosely coupled code.

I will write in this blog about many of these principles and patterns. So be around! For now, let’s try to use DI in Swift.

DI in Swift

Here is a basic DI example in Swift:

class Profile {
    private let user: User

    init(user: User) {
        self.user = user
    }
}

The Profile class doesn’t create a user; it states that it needs a user in order to instantiate it. Clients of the Profile class will need to supply the user dependency.

Swift is a general-purpose and multi-paradigm programming language. You can write Swift code in an imperative style (object-oriented programming or procedural programming) or in a declarative style (functional programming).

In Swift, we have classes, structs, enums, methods, and functions. All of these types can receive dependencies which are other classes, structs, enums or functions.

Here is an example of a function that receives a dependency:

func calculate(bill: Bill) -> Int {
    var result = 0
    bill.items.forEach { result += $0.cost }
    return result
}

Another example where a function receives another function as a dependency:

func print(bill: Bill, billCalculator: (Bill) -> Int) {
    bill.items.forEach { print(“\($0.name): \($0.cost)”)}
    let total = billCalculator(bill)
    print(“Total: \(total)”)
}

Conclusion

We only scratch the surface of DI. There are many benefits that DI enables, such as late binding, dependency inversion, extensibility, and testability. We will explore them in the next post.