Stable vs. Volatile Dependencies
Tags: swift, ios, tutorial
Published: 2022-02-16 22:51:54

In previous posts, we described dependency injection, which is a technique to inject an object into another object that needs it. But, should we inject all objects that an object needs? Of course not. We should inject volatile dependencies. We can safely directly depend on stable dependencies. This post is about these two concepts.

## Stable Dependencies
Stable dependencies contain deterministic behavior. That is, given input (or no input), it always returns the same result (or does the same side-effect).

For example, say we have a simple cart class that calculates the total of given items:

```swift
class Cart {
    private var items = [Item]()
    
    var total: Int {
        var result = 0
        items.forEach { result += $0.amount }
        return result
    }
    
    func add(item: Item) {
        items.append(item)
    }
}
```

The behavior of this class is deterministic. When we add an item with an amount of 1, we will always get 1 as the total. When we add another item also of amount 1, we will always get 2 as the total.

So, if we have, for example, a `CartViewController` class that wants to use this `Cart` class, we can safely depend on `Cart` directly.

```swift
class CartViewController: UIViewController {
    let cart = Cart()
    
    // ...
}
```

In the book *Dependency Injection Principles, Practices, and Patterns* by *Steven van Deursen* and *Mark Seemann*, they describe this rule for stable dependencies:

> In general, DEPENDENCIES can be considered stable by exclusion. They’re stable if they aren’t volatile.

I find this general rule very helpful when deciding if I need to inject a dependency or create it directly. So let's look at how to spot volatile dependencies.

## Volatile Dependencies
A volatile dependency, on the other hand, contains a **nondeterministic** behavior. You give it some input, and you get some output. But If you rerun it with the same input, you might get a different output.

A volatile dependency is volatile because:
- It depends on an external world to do its work. It’s known as out-of-process dependencies because it does their work outside of the application process.
- do a nondeterministic computation such as computations based on random numbers, date, or time
- It’s under development or doesn’t exist yet, so we don’t know the outcome of using it.

Let’s look at an example for each one of them.

### Out-of-process dependencies 
An out-of-process dependency, as the name suggests, does their work outside of our application process. That is, it depend’s on an external world that is outside of our world (the application world).

The obvious example of such dependencies is the network. When you send a request across the network, your request is processed on some external system, a server, or the cloud (which is another name for a server 😄).

For example, this `ProductsViewController` class directly depends on a volatile dependency:

```swift
class ProductsViewController: UIViewController {
    let session = URLSession.shared
    
    func fetchProducts() {
        let request = URLRequest(url: URL(string: "https://example.com/api")!)
        session.dataTask(with: request) { data, response, error in
            // ...
        }
    }
}
```

`URLSession` is a volatile dependency because it deals with the network, an outside world to our application. You will never certainly know the outcomes of the requests it sends. It may fail from the operating system side (e.g., No connection available), it may reach the server, but the server is down, or the server may respond with an unexpected response.

To decouple our view controller from this uncertainty, we should instead inject `URLSession`:

```swift
class ProductsViewController: UIViewController {
    let session: URLSession
    
    init(session: URLSession) {
        self.session = session
        super.init(nibName: nil, bundle: nil)
    }
    
    func fetchProducts() {
        let request = URLRequest(url: URL(string: "https://example.com/api")!)
        session.dataTask(with: request) { data, response, error in
            // ...
        }
    }
}
```

Now you can inject a `URLSession` with any configuration you want, not just `URLSession.shared`. Moreover, you can mock network requests using `URLProtocol` so that you can **control** the response coming from `URLSession`  either for development or testing purposes:

```swift
let config = URLSessionConfiguration.default
config.protocolClasses = [URLProtocolMock.self]
let session = URLSession(configuration: config)
let productsViewController = ProductsViewController(session: session)
```

For more about mocking with `URLProtocol`, see [this post](https://www.hackingwithswift.com/articles/153/how-to-test-ios-networking-code-the-easy-way).

### Nondeterministic computation dependencies 
The main problem with dependencies that has nondeterministic computations is that you cannot unit test them. Because every time you run them, they will produce a different result.

As an example, say we need to greet a user and tell them either good morning or good evening based on time:

```swift
func great(user: User) -> String {
    let now = Date()
    let components = Calendar.current.dateComponents([.hour], from: now)
    guard let hour = components.hour else { return "Hello \(user.name)!" }
    let isMorning = (0...11).contains(hour)
    let greeting = isMorning ? "Good morning" : "Good evening"
    return greeting + " \(user.name)!"
}

great(user: User(name: "Abdullah"))
// Output will be "Good morning Abdullah!" or "Good evening Abdullah!" 
```

Here is an attempt to test this code:

```swift
class GreetingTests: XCTestCase {
    func test_greetUser() {
        let user = User(name: "Abdullah")
        XCTAssertEqual(greet(user: user), "Good morning Abdullah!")
    }
}
```

The problem with this test is that it will succeed in the morning but fail in the evening. This is because our greet function directly depends on a volatile dependency, which is `Date()`.

So, the solution is to use DI to inject a `Date`:

```swift
func greet(user: User, at date: Date) -> String {
    let components = Calendar.current.dateComponents([.hour], from: date)
    guard let hour = components.hour else { return "Hello \(user.name)!" }
    let isMorning = (0...11).contains(hour)
    let greeting = isMorning ? "Good morning" : "Good evening"
    return greeting + " \(user.name)!"
}

greet(user: User(name: "Abdullah"), at: Date())
// Output will be "Good morning Abdullah!" or "Good evening Abdullah!" based on current time
```

And here is how we can easily test this new implementation:

```swift
class GreetingTests: XCTestCase {
    func test_greetUserAtMorning() {
        let user = User(name: "Abdullah")
        let date = Calendar.current.date(bySettingHour: 9, minute: 0, second: 0, of: Date())!
        XCTAssertEqual(greet(user: user, at: date), "Good morning Abdullah!")
    }
    
    func test_greetUserAtEvening() {
        let user = User(name: "Abdullah")
        let date = Calendar.current.date(bySettingHour: 16, minute: 0, second: 0, of: Date())!
        XCTAssertEqual(greet(user: user, at: date), "Good evening Abdullah!")
    }
}
```

### Under development dependencies 
Let’s say you want to create the user interface of a news application. You didn’t yet implement how to fetch that news, or maybe someone else is working on that. To start implementing the UI, you need at least a [stub](https://en.wikipedia.org/wiki/Method_stub) to fetch some news and drive your UI.

```swift
struct News {
    let title: String
    let content: String
}

protocol NewsLoader {
    func fetch(completion: (Result<[News], Error>) -> Void)
}

class StubNewsLoader: NewsLoader {
    func fetch(completion: (Result<[News], Error>) -> Void) {
        completion(.success([
            News(
                title: "Title 1",
                content: "Content 1 ..."
            ),
            News(
                title: "Title 2",
                content: "Content 2 ..."
            ),
            News(
                title: "Title 3",
                content: "Content 3 ..."
            )
        ]))
    }
}

class NewsViewController: UIViewController {
    let newsLoader: NewsLoader
    
    init(newsLoader: NewsLoader) {
        self.newsLoader = newsLoader
        super.init(nibName: nil, bundle: nil)
    }
}
```

Once you or someone else is done implementing the real news fetching, you can inject the actual implementation instead of the stub.

```swift
class APINewsLoader: NewsLoader {
    let session: URLSession
    
    init(session: URLSession) {
        self.session = session
    }
    
    func fetch(completion: (Result<[News], Error>) -> Void) {
        session.dataTask(with: request) { data, response, error in
            // ...
        }
    }
}

// Somewhere in your application
let apiNewsLoader = APINewsLoader(session: .shared)
let newsViewController = NewsViewController(newsLoader: apiNewsLoader)	
```

## Stable Dependency That Depends on Volatile Dependency
So we described that volatile dependencies should be injected and not directly created. On the other hand, we can safely depend on stable dependencies.

But can we depend on a stable dependency that has a volatile dependency? Let’s see if we do. 

Let’s say we want to create a view model for our news example. The view model will handle the fetching of news, so we will inject a  `NewsLoader` in the view model instead of the view controller.

```swift
class NewsViewModel {
    @Published var allNews = [News]()
    @Published var loadingError: Error? = nil
    let newsLoader: NewsLoader
    
    init(newsLoader: NewsLoader) {
        self.newsLoader = newsLoader
    }
    
    func load() {
        newsLoader.fetch { [weak self] result in
            switch result {
            case .success(let loadedNews):
                self?.allNews = loadedNews
            case .failure(let error):
                self?.loadingError = error
            }
        }
    }
}
```

The `NewsViewModel`  depends on `NewsLoader`, which is a volatile dependency, so we inject it. In `NewsViewController`, we cannot create `NewsViewModel` directly because it requires a `NewsLoader`.

One solution is to inject a `NewsLoader` into `NewsViewController`, and then the `NewsViewController` passes the `NewsLoader` to the `NewsViewModel`.

```swift
class NewsViewController: UIViewController {
    let newsViewModel: NewsViewModel
    
    init(newsLoader: NewsLoader) {
        self.newsViewModel = NewsViewModel(newsLoader: newsLoader)
        super.init(nibName: nil, bundle: nil)
    }

    // ...
}
```

As you can see, we only pass `NewsLoader` to `NewsViewController` so that we can create a `NewsViewModel`. This is similar to the [middle-man anti-pattern](https://refactoring.guru/smells/middle-man).

Indeed the `NewsViewController` needs a `NewsViewModel`, but it doesn’t have to create it. 

A better solution is to inject the `NewsViewModel` into the `NewsViewController` directly.

```swift
class NewsViewController: UIViewController {
    let newsViewModel: NewsViewModel
    
    init(newsViewModel: NewsViewModel) {
        self.newsViewModel = newsViewModel
        super.init(nibName: nil, bundle: nil)
    }

    // ...
}
```

Somewhere in your application, you create the `NewsViewController` like the following:

```swift
let apiNewsLoader = APINewsLoader(session: .shared)
let newsViewModel = NewsViewModel(newsLoader: apiNewsLoader)
let newsViewController = NewsViewController(newsViewModel: newsViewModel)
```

By the way, the `NewsViewModel` should be considered as a **volatile dependency**, not a stable dependency that depends on a volatile one. 

Once a type depends on a volatile dependency, it itself also becomes volatile. I included this section because it’s a common mistake.

## Injecting stable dependencies to pass data
The last thing I want to touch upon is that you might use DI to inject stable dependencies just to pass data.

For example, let’s say we want to display each news on it’s own screen.

```swift
class NewsDetailViewController: UIViewController {
    let news: News
    
    init(news: News) {
        self.news = news
        super.init(nibName: nil, bundle: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        display(news)
    }
    
    private func display(_ news: News) {
        // ...
    }
}
```

The news struct is pretty stable, it’s just two fields of type `String`, but we don’t know at compile time which `News` to pass.

At run time, when, for example, we tap on a news in `NewsViewController`, the news will be passed to `NewsDetailViewController`.

```swift
class NewsViewController: UIViewController {
    let newsViewModel: NewsViewModel
    
    init(newsViewModel: NewsViewModel) {
        self.newsViewModel = newsViewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    func tappedOn(news: News) {
        let newsDetailViewController = NewsDetailViewController(news: news)
        show(newsDetailViewController, sender: nil)
    }
}
```

## Conclusion
We looked at the difference between stable and volatile dependencies and that we should always inject the volatile ones. Because volatile dependencies are hard to control, you will lose control of your application if you always create your volatile dependencies directly. If you inject them instead, you will have absolute control over these difficult dependencies.

If each type keeps asking the layer before it for its dependencies, you will find yourself creating all of your dependencies at the root (entry point) of your application, and that is a good thing. 

The next post will be about the composition root pattern, a way to create and control all your app dependencies and extend or intercept them.