Ever since the SDKs for iOS and Android applications were launched, alternative ways of developing for these platforms appeared. In the beginning, these were frameworks like PhoneGap and Cordova, which allowed users to develop using HTML/JS, but then other frameworks like Xamarin.Forms, React Native and, more recently, Flutter (which uses its own language, Dart) surfaced.
One could argue that the first frameworks weren’t that good - effectively, you were only rendering web pages and your apps were nothing more than glorified web views with maybe a bit more system access than your average web browser, and they wouldn’t feel native.
They aren’t the holy-grail of mobile development though. There are some pain points, which you need to consider when you want to develop an app.
They are based on single-threaded languages. This is all good and dandy, except when you start thinking about the fact that Android phones usually have a lot of cores which don’t usually run at especially high clock speeds - the same doesn’t really apply for iOS devices, but you need to think about both, else why would you think about using a cross-platform framework in the first place… Why is this a problem, though? Well, a single thread is usually enough for simple apps, but for apps that try to do stuff in the background, like cache data for future usage or run complex computations while the user is doing something else, it can be problematic, as performance of both the main and background task will suffer.
To be fair, this doesn’t apply to Xamarin.Forms, but that one comes with its share of problems that are outside the scope of this post.
It’s true that you can have a single app that easily looks the same on both iOS and Android, even more if you use Flutter due to its game engine nature, but is that something you really want? Users expect a user interface consistent to the UI guidelines of their framework, not something that just looks good. Also, there are things that are different between platforms - you don’t have a dedicated back button on iOS, for example, and your UI needs to change and reflect that. You’re going to have a hard time dealing with designers if they expect the design to be “write once, run anywhere”.
Library ecosystem also isn’t great. Flutter and React Native are always changing, and libraries need to be upgraded to reflect that. It’s quite possible you want to use a library that was built a few months ago and wasn’t maintained since then (which isn’t really that much time), but you can’t since it doesn’t support the version of the framework you’re using, something that doesn’t really happen if you are building a native app. Also, you are going to need to upgrade your framework from time to time, which can take days of work for a developer even for a small app since there are small but breaking changes on new framework releases.
You also need to think about the features your app will use. You can do things like widgets and get low-level hardware access on apps built on top of cross-platform frameworks, but you’ll need to dive into native code, write code for all the platforms you are targeting, and write a bridge on top of that so it can be used from your cross-platform code. If you need to use features that are only available on a platform or are not supported by the framework of your choice, you’re going to spend a lot more time than if you went native in the first place. A notable example is that you can’t currently develop apps for watchOS using a cross-platform framework, and support for Wear OS is also quite limited - you need to dive deep into native code and even create entirely separate apps in both cases.
Most of my points have been focused on performance and features. This is something that you may not really care that much about if you are building a prototype or a simple app. There are, of course, positive arguments to be made for each one of native and cross-platform.
There’s no doubt that the time to market of a single cross-platform app, one that is simple and doesn’t need to do much out of the ordinary (like access to specific hardware or functionality only available on a single OS), will be much smaller on a cross-platform app than when building two different apps.
Prototyping time is also usually faster in Flutter and React Native when compared to native languages, since you can use features like Hot Reload which allow you to see changes you make to the UI on your code in real-time. This is not so much of an issue with newer technologies like SwiftUI on the iOS side of things, but we’re still not quite there yet.
Additionally, the performance penalty may not be that noticeable if your app doesn’t do much more than display data or allow the user to interact with data in a simple way.
There are examples of apps that started out as not much more than being glorified web views then moved on to full-blown apps - the Facebook app being a good example of this - so that’s also a possibility if your use case allows.
In conclusion… if you need something out in the market in the lesser time possible, for both iOS and Android at the same time, cross-platform frameworks may be a good choice. But if you want instead a small, performant, capable app you can rely on…
This article was updated on April 22nd to fix some minor grammar issues.