The Dependency Inversion Principle is the last one from SOLID stands for decoupling the system into independent modules. It means that one part of a system should not depend on another one directly.
Let's see an example.
On the one hand, we have a ViewController
which is the part of UI/Presentation Module, and on the other hand, we have some data source which can be a part of Networking/Persistence Module. Let's say it is an URLSession
object.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
URLSession.shared.dataTask(with: url) { data, response, error in
// Do something with response
}.resume()
}
}
This code can be represented by a diagram
The UIViewController
depends on URLSession
concrete implementation. And it means that URLSession
can't be replaced with another Network client such as Alamofire without changing the Presentation Module.
Solution
This problem can be solved with Dependency Inversion just by adding another abstraction between concrete implementations. Some protocol
that can live in Presentation Module and the Network Module can conform to it.
and the code above will transform to
protocol HTTPClient {
func load(url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void)
}
class ViewController: UIViewController {
var client: HTTPClient!
override func viewDidLoad() {
super.viewDidLoad()
client.load(url: url) { (data, response, error) in
// Do something
}
}
}
Now you can have two separate implementations of HTTPClient
protocol, and they both can live in Network Module
That's the way you invert the dependencies from one module to another, and now you can easily switch between two concrete implementations of HTTPClient
from Networking Module.