For a while now we’ve tried to maintain consistency in our code by using a code style guide. One area we haven’t had any specific guidelines around is code architecture. For a recent Android project, we used the Android Architecture project as a guideline. We focused on the use of MVP to separate business logic from basic view operations. We also looked at the use of repositories and data sources to handle the data side of the app.
This gave us a good foundation for the app which we intend to continue using for future Android apps. As a result, we needed something similar which we could use as a guideline for our iOS apps.
We’re still working on an example project but we’ve settled on a few principles which I’ll outline.
We started off by looking at MVVM as an alternative to the MVP architecture that we use in Android. From our brief look, MVVM seems to rely on Rx and the use of Observables. We aren’t ready to investigate these yet, so we decided to keep the platforms consistent and use MVP on iOS as well.
In our younger days, we would have implemented all the network requests in a single DataManager class. A class which may also have handled synchronisation with a local database. Obviously, this leads to a large class with many responsibilities.
Now that we are more wrinkled, each repository handles one network request. There is a remote data source for the request implementation and a local data store if necessary. The repository contains the business logic of when to use the remote or local store. It also contains any memory caching.
Dependency injection is another area we want to be consistent in. So far we’ve only used constructor injection. This is where you pass in the necessary dependencies using the constructor or init method. This stops components from having implicit or hidden dependencies on each other. It also helps with testing as you can pass in mock objects.
To help with the management of dependencies, we’re using Swinject. At the moment, we only use it for managing repositories and data sources. The container scope allows dependencies to act like singletons while avoiding some of the issues that can occur. For example, tests affecting each other because the singleton has been re-used.
All of these decisions have taken time and effort to explore. If you want to get a jump on that process here are a few resources we have found useful:
Search over 200 blog posts from our team
Subscribe to our monthly digest of blogs to stay in the loop and come with us on our journey to make things better!