Building Mobile Apps for Scale
Mobile Applications have become the hottest commodity in Software. Almost every business out there wants to have a mobile app. Mobile presence is fast replacing websites, and everyone believes that a healthy mobile presence will bring on more customers to their platform, business, etc. Why would someone download your mobile app is an entirely different question that I’ll detail out in a future post. For now, I’ll stick to keeping this post about building your mobile app in a scalable fashion.
For the sake of clarity, lets define Scale.
Scalability is the capability of a system, network, or process to handle a growing amount of work, or its potential to be enlarged in order to accommodate that growth
When you start developing a mobile application or for that matter any application, the last thing on your mind would be scale. You would look at quickly building out your application to see how your idea flies, and whether people would actually be interested in using it.
While it seems rational to do that, ensuring that your app works at some decent scale is something you ought to keep in mind even in the initial phase of your development cycle. Nevertheless, once you are out of the idea phase and have established that your app is actually going to work, you would look at scaling this. As your user base grows, it increasingly becomes important to ensure that the quality of the app you ship and its features work seamlessly.
For some folks, the idea of a mobile app scaling might seem kind of odd, given that scale is mostly associated with backends and not standalone clients such as mobile apps. Let me assure you, scalability does apply to mobile applications as well. In the following post, I’m going to detail out a few techniques that will help you go that extra mile.
In the broader sense, if any of the following points apply to you, read on. If not your probably, not sure what you want, or you’re wasting your time.
- Your application is not a fun hobby project
- You have real users out there
- You’re making money out of your application
- Your user base is increasing by the day
I’m going to talk about a few specifics that will help you go that extra mile.
- React Native vs Native
- Layered Architecture
- Remote Config
- Error Reporting
React Native vs Native
In recent times, this is the hottest debates on what to consider when it comes to building for mobile. React Native stormed the mobile app eco-system a few years back and has since grown to have a thriving community of developers who are only working on making it better. There are enough articles written on the subject of React Native, its internals, etc. and my intention here is to only help you make your choice.
A few questions you need to ask before making this choice
- Is your app heavy on Native Code? i.e. do you need to write abstractions for everything you’re building? Typical, when your app is more than just a view-based representation of data. eg. video editing apps, calculators, etc.
- How important is it for you to share code between Android and iOS?
- How frequently do you update your app?
- How much do you care about developer productivity?
Next, consider a few more points.
- Most businesses are realising that it’s becoming increasingly difficult to support two platforms.
- Most common apps can be built using React Native
- The community is thriving, with a lot of components open sourced (link)
Layering your application into different logical parts will help you focus on one thing alone while building it. Consider the example of a simple twitter client. A naive way to build this would be to have a single activity or view controller which has a list view or table view and also contains all the service calls, etc. in that single class.
So this one class does all of this
- API calls
- JSON deserialisation
- Manipulates your view
- Handles your TableView or ListView callbacks
While this would definitely work, it’s probably not the best way to build out an application that you expect to expand upon.
It makes a lot of sense to split up your application into functions of responsibility. (Remember SRP from SOLID principles of OO design).
Separate out your application into
- A service class which handles all your API calls, JSON deserialisation, etc.
- A View Controller which could act as your TableView Delegate and DataSource as well. You could alternatively separate these out too.
This simple separation will ensure that you focus on the aspects that are important to the part that you’re working on at that moment. Further still, it will help you divide the work when working in a team, and also help you write more testable code.
There are better ways you could separate your code out beyond this point. You would have heard of MVC, MVP, MVVM and other such upturns of UI programming, You could choose to employ one of these patterns. While I personally prefer MVVM, MVP or Flux (will tell you why, in a future post). Using a well-defined pattern for your application ensures that you are using battle-tested techniques that will ensure that various aspects such as testability, code separation, DRY principles, etc. are adhered to.
The next step would be to create a generic wrapper over your network classes. This will help you separate out API auth, allow you to add generic headers, etc. While this might not seem as a must, I’ve found that it helps a lot in the long run.
You should also consider building abstractions for
This will help you chose between different data store choices later. You can abstract out whether you chose to persist data in a Sqlite db, or a simple file store, for that matter, and wouldn’t have to change all your code, should you decide to change the underlying storage.
Cache will always be necessary and unlike Local Storage, need not be persistent. It still helps to have a MemCache + DiskCache abstraction to help you deal with data that’s needed across the app.
This will help you seamlessly add other analytics providers to your application easily. Trust me on this, it goes a long way in having this abstraction.
You would always find the need to track Performance metrics, whether it be measuring response times as perceived by the client, or view load times. You could send this data over to Google Analytics, or Fabric, or build your own backend to track this. Once again, consider building an abstraction for this as well so as to have the flexibility to chose between a new provider.
Okhttp gives you a nice way to create request and response interceptors that you can use to manipulate your requests and responses. As most of your auth or shared info would reside in http headers, these will make your application a lot cleaner.
Remember to build abstractions on top of the base client, whether you chose to use okhttp, AFNetworking or any other http client. Its your wrapper that’s important here.
Logging is a very important part of your app, and by providing an interface for handling your logging needs, will go a long way. Do not resort to using NSLog or Log directly. There are a suite of libraries that help with this.
You will always find the need to use some form of a config for your app. Either to turn on and turn off features or to render some static content such as a settings menu, etc. It would make sense to pull this info off from a remote source, rather than hard coding this in your app. Trust me, you will always find the need to change some of these values some day. And you wouldn’t want to do a native app push for that!
This config need not be too complex. A simple JSON somewhere that you can update and access over the web would suffice. For starters, you can pull this off of Firebase and then grow on to build this in-house. (Quick-tip: Consider abstracting out your datastore on the server side from your client. You could build a simple API wrapper that caches and delivers your config back to your clients).
Here’s a small suggestion when describing views that you intend to configure remotely, this is based on how JSX defines a component tree.
This was a suggestion from Sunil Pai and I have since used it in almost all my projects.
Whenever you ship an app that’s used by a lot of users, you should always be aware of the health of the app. Your test suite and the set of devices on which you performed your QA are not enough. There are a multitude of device variations and a whole lot more factors that can affect the outcome of an operation on your app. Things like network, memory, battery, cpu, model, OS version, etc. play a very important role in determining how your app behaves. Apart from that, our unit tests or functional tests can only cover so much. Error and Crash Reporting tools will tell you when your app crashes. Couple that, with some good logging, you can easily zero in on the reason behind a crash.
Popular tools for the job include
If you are looking for a tool to help you specifically with React Native (JS based exceptions), I’d suggest using Bugsnag.
More Reading (React Native)
- React Native — https://facebook.github.io/react-native/
- Internals of React Native — https://www.youtube.com/watch?v=hDviGU-57lU
- A Quick guide to learning React Native — http://www.reactnativeexpress.com/
I will leave you with a link to a talk I gave last year at Droidcon, Bangalore about how we at Myntra switched to React Native and what it meant for us.