You may have heard about the Dependency Inversion principle from the OOP’s SOLID design guidelines.
The idea behind this principle is that an implementation should only be dependant of interfaces and abstractions - never of another implementation.
It also brings us the concept that a higher level module should never be dependant of a lower level module.
Protocol to the rescue
In Swift, with protocols, you can get rid of any implementation dependency and instead rely on the implementation of a protocol.
Real life example
Let say you have a network stack :
class AppServices {
// AppServices will keep an instance of network and any other lower stack
// that must have a shared instance. Eg: Persistence.
static let network: AppNetwork = AppNetwork()
}
class AppNetwork {
// This class is in charge of auth, holds the token and requests queue
func fetch(_ operation: NetworkOperation, completion: NetworkCompletion)
}
And a controller that requests a list of data from an API, using the network stack :
class ViewController: UIViewController {
func loadData() {
AppServices.network.fetch(ListData) {
self.view.refresh(listData)
}
}
}
The Network stack is a low level module, and the ViewController is tightly coupled with AppNetwork’s implementation throughout AppServices.
We can solve this dependency issue by recreating the Network as a protocol :
protocol Network {
func fetch(_ operation: NetworkOperation, completion: NetworkCompletion) {}
}
and make the AppNetwork conforms to Network :
class AppServices {
static let network: Network = AppNetwork()
}
class AppNetwork: Network { … }
Now, AppServices wont hold a AppNetwork implementation but an abstract implementation of Network.
Another benefit of this is you can easily mock your networks stack, simply by replacing the implementation of Network.
class MockedNetwork: Network {
func fetch(_ operation: NetworkOperation, completion: NetworkCompletion) {
completion(stubResult, fakeError)
}
}
// make sure you set the static as a `var` and not a `let`
AppService.network = MockedNetwork()
Following this principle in this specific scenario is a very nice and clean way to improve the testability of any network dependant code, the flexibility and the scalability of your network stack, and you can remove any coupling of your app's controllers or view to your app's lower stacks.
I hope you enjoy this little post.
I chose to cover the dependency inversion principle as I feel it is a very useful and important principle to follow while coding and a great way to improve code and product's quality. I would be happy to answer any question in the comment section or feel free to reach via my Twitter ;)
Happy coding!
Top comments (0)