Encapsulate Framework Implementation in Your iOS Application
Hide any frameworks implementation from your features.
When we building an application, sometime we need to use frameworks or libraries to help us develop the feature. It is an efficient way because we don’t have a much time to write all codes by ourself. For example when we need an offline feature in our app, we can use so many framework options such us Core Data, Realm, NSCache, etc. Or you may need to use StoreKit in order to implement In-App Purchase. Maybe you also need to use open-source library in community, like Texture to building the UI.
In fact, we are tight-dependent to any frameworks. But the question is, how we can make our feature in our application is decoupled from any frameworks implementation? Maybe currently you are using Realm for offline usage in your app, but your team decide to use Core Data in the future, or maybe you want to implement unit testing in your app but you have no idea how to test a feature that implement StoreKit, Vision, or whatever it is.
Make your app decoupled from any frameworks implementation has so many advantages. You can switch the framework without breaking the feature implementation, make your app tolerant of changes without need a huge effort, and maybe make your app testable so you can speed up the feature development iteration. Your product manager would be really happy with this :].
So in this article, I will try to show you how to encapsulate the framework implementation and make it decoupled from your features. I have a simple application where user can manage their daily tasks. It use Core Data to persist the data, and with this design system, you are able to change the local storage framework implementation in the future.
Take a look at this project repository and feel free to clone it.
This is a simple app that allow us to create a task with a deadline date. You can finish the task before the deadline date, and you can filter the displayed tasks based on the tab menu. Whenever you create a task, it will saved by using Core Data behind the scene.
To make it simple to explain, let’s say we have two features in this app which are Task List and Add Task. Both features have presenter layer to manage the presentation logic. The presenter layer need to get the task data from the data infrastructure layer. Because in this app using Core Data as a data source, this is the infrastructure layer code look like:
That’s just simple custom Core Data setup, where you can define the store type by configure the value from isMemory. If the value of isMemory is false, then it use default type of store which is using SQLite behind the scene, otherwise you use NSInMemoryStoreType for unit testing purpose.
That’s the Task entity from the Core Data side, which is the NSManagedObject. It consist of the properties from the Task that we already defined.
At this point, our Core Data infrastructure is ready to use. But we also need to think, if we using this entity in our app side, it is definitely tight coupled.
Imagine if are you using this entity directly to your app and in the future your team decide to change the data layer infrastructure using Realm, it is pain to do the migration. Because maybe in Realm or any other frameworks, they also have some kind of base class to create the entity, such us NSManagedObject in Core Data.
The simple way to solve this problem is, we can create a new model that live in the app side as an interface model. So the concrete entity from any frameworks can be converted to this model.
With this approach, our app doesn’t have to know the concrete entity implementation. It has flexibility that doesn’t care what happen behind the scene. Let’s make the Task entity to conform to TaskModelConvertible protocol.
That’s simple but elegant, right? :]
Then we can move to presentation layer. The presenter from both features somehow need to talk to Core Data infrastructure, to have an interaction with the data right? To make the presenter decoupled from the Core Data infrastructure, you need to define the abstraction using protocol as a contract between them.
The contract is done. Your presenter only need to interact with Core Data infrastructure with those functions. Then we can implement dependency injection in presenter implementation:
At this point your presenter is ready to consume by the view side. But we don’t have a concrete implementation yet right? how we can initialize this presenter then?
Don’t worry, we already have the Core Data infrastructure right? We can create an extension from the Core Data infrastructure and conform it to the TaskViewDataStore protocol.
Pro tips: If your app is modular, this extension should live on the composition root module, commonly in Main application module.
Hooray, we have the concrete implementation from the TaskViewDataStore protocol and finally we can compose our object together :].
Awesome!, maybe it is just simple app, but we create it like a pro software engineer :]. You don’t have to worry anymore if your boss ask you to change the framework implementation, your features would be still good to go.
With this design system, it is also easy to implement the unit testing. You can create a spy object for the TaskViewDataStore protocol.
Finally you can inject the spy to your test cases like this:
Finally it is done, although I am not creating all the unit test yet in the project :].
On these day, we are often need the framework to help our job as a software engineering. Know how to create a design system that make your app loose couple from the framework implementation is important. So many advance topic in this article, if you confuse please don’t worry and keep practicing. Start with a basic foundation like dependency injection and dependency inversion principles. That would help you a lot to build a scalable system. I also have an article about how we can write a unit testing to test the Core Data here. Thank you for reading this article, please let me know if you have a feedback or question. See you in another article!