Most ‘new business’ requires a mobile app. Developing for iOS and Android can be time consuming and expensive such that people are often tempted to turn to cross platform tools to write once and run on multiple platforms.
Cross platform tools are beguiling. They offer easier, higher level, development and the ability to run on both iOS and Android. However, apart from a limited subset of apps, cross platform tools will disappoint in so many ways. You might even end up expending more rather than less development effort.
This article takes a short look at the history of cross platform tools, examines why cross platform tools fail and explains why cross platform developers of non-trivial apps will always face an uphill struggle.
A Short History of Cross Platform
The first cross platform mobile development platform was probably AppForge. Pre iPhone, it allowed you to develop for Symbian, Windows Mobile, RIM BlackBerry and Palm OS from one code base. I developed apps using this powerful and simple tool. Despite the cross platform claim, Symbian and Windows Mobile needed slightly different code due to the different user interfaces. Nevertheless, much of the remaining code was common. New features lagged those in the underlying OSs. Oracle acquired AppForge in 2007 and dropped all existing developers. These three learnings live on in subsequent cross platform tools:
- It’s difficult to support different UIs with one set of code. If you need true native look and feel (most end user expect this) you need the native OS widgets and you will need different UI code for each OS.
- The underlying OS platforms evolve and it’s a mammoth task for the cross platform tool developers to keep up.
- The cross platform tool is only as strong the team/company behind it. If they drop the tool, your apps will die.
Since then there have been many tools such as PhoneGap/Apache Cordova, Rhomobile, CodenameOne and Appcelerator Titanium. More recent entrants include Xamarin, React Native, RxJava and Flutter (just for the UI).
During my mobile career I avoided cross platform tools and ignored the ‘future is the mobile web’ mantra. These tools were more trouble than they were worth. Mark Zuckerberg famously said he regretted using HTML5 rather than native. AirBnB regretted using React Native.
Cross platform tools are still failing. Flutter has over 5,000 issues on GitHub and developers say it looks good but is painful. RxJava used to be very popular on Android but it’s dying now mainly due to Kotlin which is the ‘latest thing’. Developers have a tendency to flock to the latest thing without deep thought why and who is promoting it.
There are fundamental problems implicit in the implementation of cross platform tools. While some of the problems can be mitigated with a large amount of development effort put into a tool itself, it’s usually the case that it’s not financially viable for the tool provider to maintain this level of effort long term. Long term effort is required because the underlying native iOS and Android platforms are being regularly updated and the number of target devices is getting ever larger.
For users of these tools, what might initially seem easy for a ‘hello world app’, quickly becomes complex when you start adding real world features.
You can’t evaluate some piece of tech before you’ve built something large with itRxJava Lessons
Abstraction causes loss of performance – As soon as you base something on something else, you loose performance when going from one to the other. The degree to which you lose performance depends on how well the higher level tool is written and in many cases, writing efficient code is a lost art. Abtractions added by the cross-platform tool developers help them to undertand the code better but can cause significant loss of performance.
Loss of OS level performance features in the cross platform tool – iOS and Android have fine tuned APIs for parallel threading and higher level features such as scrolling, gesture support, animation and view recycling. Many of these features use hardware acceleration. It’s impossible or too complex to bring these features out in a cross platform way. Emulating these things at a higher level produces visibly slower user interfaces.
Adopting different idioms loses performance – For example React Native depends on serialised and deserialised JSON that can be can be costly for data-intensive problems. It’s also single threaded which constrains throughput.
Performance doesn’t just manifest itself through speed. Size is also important. Cross platform runtimes that support the super-set of all features can be very large. Often, these have to be bundled with the app irrespective of whether features are used.
Look and Feel
Web based cross platform tools are rendered using a subset of the usual web browser called WebViews. It’s possible to get HTML close to the look and feel of Android and iOS but it takes a considerable amount of effort due to web browser browser fragmentation.
Another issue is that iOS and Android look and feel changes over time when Apple and Google update the OS. If the cross platform tool is faking rather than using actual OS widgets then your app might suddenly look strange as it isn’t faking the right thing any more.
In order for a cross platform tool to render using the actual native widgets, it needs to keep up to date with the variants available in the variants of underlying platforms (Android and iOS versions and manufacturer variants thereof).
Features are facilities in the underlying platforms that need to be made available via the cross platform tools. This is usually achieved via a runtime that maps between the two or via cross-complication. Either way, there needs to be code in the tool that maps each feature.
This mapping is also required for the majority of tools that use WebViews. While access to features direct from web browsers has improved slightly over the years, Apple and Google haven’t improved their web browsers to allow the browser access to many APIs. Hence, bridging code has to be provided for each feature by the cross platform tool.
So what are the features? Here’s a non-exhaustive list:
Navigation – Graphics (manipulation) and acceleration, primitives to the screen (line, square, circle etc)
Phone features – the file system, using the camera to take photos and video, Bluetooth, answering incoming calls, accessing the phone contacts and calendar, dialling a number, send an SMS or MMS, acting on an incoming SMS, detecting the start/end of a call, vibrating the phone and manipulating phone Internet access points.
Specialist Libraries – Manipulating audio/video in real-time.
Programming idioms – Doing background processing, starting at boot, setting future alerts, long running background process, offline
The problem is that apps often start with simple features and more complex features usually get added later. It’s at the later stage people realise the platform can’t support an essential feature and development has to start again on native.
There has been, and there still is, a false expectation that html abstracts away underlying platform fragmentation. This is wrong. It’s just you can’t get to the underlying platform fragmentation so you don’t see it. Once you write real applications within the browser using bridging code you get exposed to similar issues (differences) that make native development difficult.
Web browser WebViews actually bring extra fragmentation due to largely differing support for html. For example, Niels Leenheer has a great presentation that explains how browsers vary across Android versions, devices and phone manufacturers. The consequence of this is that getting any non-trivial WebView-based app to work across many device types is very difficult. The many 3rd party companies creating app creation tools based on web technologies face an uphill battle – as do people using their tools.
Breaking Changes in the Native Platform
The underlying iOS and Android native APIs evolve over time. APIs become deprecated as newer and better ways are provide to implement features. Sometimes breaking changes occur, particularly when Apple or Google remove features for reasons performance or security. Permissions and background processing are example areas where what could be done in the past can’t now be done on newer versions of both iOS and Android.
Similar breaking changes can also occur when the platform depends on third party libraries that aren’t kep’t up to date.
This means that someone using your app might update their mobile operating system version and the app stops working. Now, this is the case with any app, not just cross-platform. It’s a case of the app developer planning well ahead to test their app against new versions of the OS. The problem with cross platform tools is that the app developer themselves can’t fix their own app. It relies on the cross platform tool provider keeping in step with new mobile OS versions. This requires a large amount of effort and usually doesn’t get completely gone.
On Android this used to not be a problem in that apps could be released and run forever on old versions of platforms and old versions of the OS. In the past few years Google has advocated using newer versions of the OS when publishing app updates. This is a dependency nightmare for all app developers and especially so for those using cross platform tools that aren’t updated regularly.
Cross platform apps tread the fine line between acceptance or rejection when submitted to the Apple App Store. The problem is that Apple reserves to right to reject apps that are not ‘app like’, created using an app generation service, cross compiled or use external application code. Apple’s view is that HTML5 apps are best delivered to using Safari rather than through an app. Apple is continually changing the App Store terms and conditions. What might pass today might get rejected tomorrow as an app update.
Using cross platform tools is a shortcut that results in unforeseen complexity and restrictions. While these tools can be great for simple apps, apps with a short life or prototypes, if your app is critical to your business it should be built with the native iOS and Android tools and languages.