OVO Tech Blog
OVO Tech Blog

Our journey navigating the technosphere

OVO TECH
Author

Share


Tags


On Refactoring. Part 1: Data sources

OVO's iOS mobile application is - on the surface - very straight forward: a user can log in, submit a meter reading, download their statements and compare their energy usages with national averages. However, the app was built in such a way that we found ourselves with difficulty adding new features, and difficulty debugging customer issues. We'll see how we managed to create a separation between UI and Data...

The Legacy

OVO's iOS mobile application is - on the surface - very straight forward: a user can log in, submit a meter reading, download their statements and compare their energy usages with national averages. However, the app was architected in such a way that we found ourselves with difficulty adding new features, and difficulty debugging customer issues. The core of the application was build on the MVC pattern with a Synchronisation system persisting in CoreData and the lack of separation between Data and Presentation was slowing down the implementation new features

The Problem

The first step was to create a separation between data and ui. The legacy application had the issue that there was no clear separation between the views and the data layer.
The legacy approach relied on notifications to let the view controller know that data is loaded.

Old Way

The view controller requests a data model.
The model is not present so the view controller requests the Sync Manager to get it.
The sync manager gets it, saves it in CoreData, and sends a global notification, accessible to all of the app.
The view controller requests the model again.

Confusing? Yes it is.

This solution relies on notifications to get the data, which brings it’s share of new issues. The view controller has to subscribe to notifications when it’s visible and unsubscribe when it’s not. The main problem is that some components can listen to the notification and very quickly it’s hard to know who’s doing what.

We wanted a new approach that satisfy these criteria:

A solution

We decided to start with an easy solution. With the help of a coordinator (Article about it coming soon), the data is being loaded from a data source and passed to a view controller.

A Better Approach

The coordinator is in charge of requesting the data and passing it to the view controller. This creates a very testable application. Each components can be taken as a unit and tested independently.

Now as the application should be working offline we decided to keep the CoreData stack. But we wanted to seclude it from the rest of the application so that in the future we could replace it.

So instead of having a cascading model we went for the composition of data sources.

It is important to note that all the data sources for the same "model" have the same interface:
get(T)

Let’s take the example of the Meter Reading data sources.
The flow is following

A Betterer Approach

The approach we took was to keep our data source very small and doing only one thing (SRP). We then compose them as one as the have the same interface

So we implemented a "redundant" data source that decides how to get the data; from the api or localy.
In the example, the redundant data source asks the api data source for a model and on success passes it to the local data source that stores it (using core data) and meanwhile passes it to the requesting coordinator. In case of network failure, it will take the model from the local storage.

The coordinator is still talking to one data source and regardless to where the data is coming from (remote or local) passes it to the view controller for display. This simplifies a lot the view controller: it just needs a model, and no need to handle the network or local issues.

Then we wanted to add an in-memory cache to speed up the application. Taking the same approach, the implantation was done in couple of minutes:

A Bettererer Approach

Conclusion

In conclusion, having building block data source allows us to have a very small class in charge of a single operation. Composing it allows us to create a more complex persistence system that synchronises the local storage from the api without the application knowing what’s happening. For the application, the data source is doing one thing: Asynchronously getting a model from a source.

Does it answer our requirements:

Author

OVO TECH

View Comments