From a monolithic cross-platform to a modular native app
Roughly four years ago, in April 2017, the CTO of FAIRTIQ asked me to take the lead on the modularisation of the FAIRTIQ app. The goal was to extract the tracking logic from the existing app and to put it into a Software Development Kit (SDK) making it usable by other apps.
By that time, I was working for a consulting company. I had a good understanding of the FAIRTIQ system since I worked as a backend developer consultant and Scrum master during the implementation of the first version in 2016. Even though I wasn’t an expert in mobile development, I had some basic knowledge in both iOS and Android gained during other projects as a consultant. As such, I accepted this new challenge with enthusiasm.
In this blog post, I tell the story of the transition from the first cross-platform app implemented with Xamarin to a fully native FAIRTIQ app. I’ll highlight some of the unexpected issues we had to deal with along the way and the changes of direction that the project took. Finally, I’ll give an overview of the current state and I’ll list some lessons learned during this project.
If you are not familiar with the FAIRTIQ product, I suggest you first read the blog post: The Journey of a Journey that describes in detail how the FAIRIQ app and post-price ticketing work.
When we were about to release our very first version of the FAIRTIQ app, we had good reasons to think that competitors were working on very similar apps. Being first to the market with both an iOS and an Android app was a matter of survival.
In order to move fast we chose Xamarin as a way to share code between the two platforms. In addition, resources were at hand: FAIRTIQ had formerly collaborated successfully with my then employer on another Xamarin project and the very same people would be available.
The FAIRTIQ app was released publicly on iOS and Android after just four months, whereas the competitor's app followed a few weeks later with only an iOS prototype available and to only a closed user group.
The rest is history; FAIRTIQ's innovative post-price ticketing gained popularity and caught the attention of public transport operators across the nation. It became obvious that we'd need to offer our core technology in the form of a mobile SDK to be embedded in other, existing apps. At that time, if you wrote an SDK in Xamarin, you'd only be able to integrate it into Xamarin apps. That would have restricted our target market a lot. Actually none of our SDK integration clients to date use Xamarin.
A hybrid architecture
We started by defining what part of the logic from the existing app had to be moved to the SDK. Then, we analysed how to extract that logic from the Xamarin implementation. We knew that interacting with native code from the Xamarin platform was technically possible. Therefore, our idea was to keep the existing Xamarin UI and to replace the Xamarin logic with the native SDK one feature at a time. The idea was to save a lot of time because the re-implementation of all the existing screens was avoided.
The main features we had to implement in the native SDK were:
- The check-in/tracking process: Once the user starts a trip (ie. checks in) the SDK collects position and activity information and sends it to the backend system.
- The check-out process: At the end of a trip the user stops the tracking activity (ie. checks out) and the SDK makes sure that a precise location, where the journey ended, is known.
- Historical data access: The SDK gives access to a summary of all past journeys.
As a starting point and proof of concept, we looked to replace what we identified as the easiest, most isolated piece of code: the server clock synchronization, which, in a nutshell, was required to prevent some fraud cases.
We started with Android. The implementation of the server clock synchronisation proved to be a success and we released it in the Xamarin app a couple of weeks after the project started. At that point, we were starting to get enthusiastic about the future success of the SDK implementation.
Unfortunately, the implementation of the same feature was much more complex on iOS. Swift’s API, the (then) new iOS language, was not yet stable. As such, Xamarin did not provide native converters to easily communicate with Swift. As a result, we had to create converters ourselves. This led to manual work that was long, tedious and error prone; but we did it. It took more time than for Android, but a couple of weeks later, we had a version of the Xamarin iOS app that had integrated a native SDK.
Our motivation picked again as we started to implement the first big feature: the check-in/tracking process. Unfortunately, digging deeper into the Xamarin code showed us that the code was a mess and had tons of tech debt, which made it impossible to split out to the SDK. This is invariably what happens when you rush for a release.
After several weeks of work, we had to face reality. The entire app had to be re-implemented from scratch.
A modular native app
Since all the code had to be rewritten anyway, we decided to go fully native. This allows to rely on the most efficient and secure APIs, e.g. for the localisation and the activity recognition and it makes the sourcing for mobile experts much easier.
Around the time we started the implementation of the native apps, our first client started to integrate the SDK in its app. The integrators started asking many questions about the interface of our SDK that forced us, but also helped us, to define an understandable and clean API. Defining a clean interface allowed us to become much more efficient at implementing both the SDKs and our new app.
Having said that, this new setup generated new challenges. With two apps in development and one cross-platform application in production, we had to deal, in parallel, with:
- The implementation of two SDKs in completely different languages;
- The implementation of two apps that needed to be similar to the existing one, but relied on a totally different API and languages;
- Fixes of existing bugs in the apps on production; and
- The implementation of new features / change requests in the production apps.
All this work was successfully handled by our three mobile experts, helped by two external consultants. They all deserve much respect for this!
Today, three years after the launch of the new native app and the underlying SDK, we can say that the challenge has been addressed. Indeed, The native apps provide many more features nowadays. They are available as two different applications in the stores, one for the public and one to run experiments with closed user groups. The FAIRTIQ Apps are used not only within Switzerland but also in several regions of Germany and Austria. Even more important, the amount of reported bugs drastically decreased compared to the cross-platform app and the App stores rating continuously improves, reaching more than 4/5 stars on both platforms.
The SDK is also a great success, since it is not only used by the FAIRTIQ apps but also by the main application of SBB (swiss national railways) and by RMV (Rhein Main Verkehrsverbund) in Frankfurt. Currently, the FAIRTIQ SDK is being integrated inside 5 additional apps. One of these is the app of a former competitor.
From a personal point of view, here are the lessons that I learned from that project.
First of all, rapid implementation can sometimes be necessary. In this case, if we would have done a cleaner app from the beginning, we would have missed our target release date and possibly would not be around today. Having said that, taking shortcuts leads to tech debt that will hit you sooner or later. It sounds obvious, I know, but experiencing it is a very good way to really understand how painful it can be.
The second lesson I learned is that one should not spend too much time evaluating if the solution to a problem is feasible before having a really deep understanding of the problem itself. In this case, we had the following problems:
- How to integrate native code inside an Xamarin app; and
- How to extract a common API from the existing app.
We started not only to analyse the first part of the problem but we spent a lot of time solving it. If we would have first analysed the second aspect of the problem, we would have seen that extracting an API was impossible. This would have saved us a significant amount of time and resources.
It took about two years since I started to think about writing a blog post about this project. I probably needed this time to take a step back and allow my thoughts to settle. Today, I’m still working at FAIRTIQ, and since it became a bigger company, I don’t spend as much time working with the apps and the SDK as before. I’m nevertheless a strong ambassador and supporter of the FAIRTIQ apps, and enjoy the amazing and innovative new features regularly released.