)
White paper
Native apps for the price of a cross-platform solution: Kotlin Multiplatform
For a long time, mobile developers had to choose between two paths to get their apps into the hands of their users: native or cross-platform. Both approaches have their unique strengths and challenges and depending on requirements and available resources one comes out on top of the other. Kotlin Multiplatform brings a third choice to the table, combining the strengths of both while removing many of their shortcomings.
Native vs. Cross-platform: Nothing is perfect
Let us look at what native and cross-platform stacks have to offer. While the specific benefits and drawbacks have different impact from project to project, here is an overview of some key areas where native apps and cross-platform differ.
One of the most common arguments made in favour of native apps is the performance of the app. Native apps are working directly with the operating system, resulting in smoother animations for apps, higher performance in games and a reduction of the app’s battery impact.
Cross-platform approaches differ in this regard. While React Native uses a translation layer to execute the abstract code written in slower JavaScript on each platform, Flutter on the other hand is a fully compiled app written in Dart. This means Flutter can achieve near native performance and can compete with the native apps in this regard.
Some things can only be done in native code because they require direct access to the phone’s hardware. Common examples are NFC, Bluetooth, or certain forms of video playback. While cross-platform frameworks come with wrappers for the most common system features, in many cases cross-platform solutions must work around limitations and restrictions. Native apps can make full use of the system’s features as they work with software directly made by Google or Apple.
Native apps look and feel “right” for the platform they were built for because they make use of the UX and UI patterns users know and love. When designing cross-platform apps, it is often hard or impossible to make an app feel at home on different platforms. Users can perceive apps that are not adapted well to their phone as lower quality compared to native apps.
One thing is clear: writing two apps is more work than making one app to rule them all. This is where cross-platform apps shine: they are often the most cost-effective way to get an app on all common platforms.
A shared codebase for all platforms makes it easier to maintain an app. The app is guaranteed to look and behave the same across platforms. Also, bugs often show up on all platforms and can be fixed once, compared to different bugs on different platforms that all need separate investigations, fixes, and new updates.
For some apps security is of the utmost importance. Banking, and medical apps – our bread and butter – are excellent examples of this. The difference comes more from the way the app functions: cross-platform apps are making use of a large intermediary layer provided by the cross-platform stack. More software always means more security relevant bugs and a larger potential for security issues.
To sum up our discussion, both native and cross-platform apps have their ups and downs sides. But what if we want a snappy app with all the features for cheap?
Kotlin Multiplatform: The Best of both worlds
This is where Kotlin Multiplatform (KMP) enters the picture. Kotlin has been the native language used for Android development for half a decade and is the standard among Android developers.
In recent years, JetBrains, the makers of Kotlin, expanded the scope of the language piece by piece and added support for more target platforms. Next to Android, Kotlin/Native allows Kotlin code to be compiled to native code for iOS, macOS, Linux and Windows. Kotlin/JS and Kotlin/WASM convert Kotlin to be usable on the web.
This immense set of capabilities is tied together with Koltin Multiplatform (KMP): it allows a shared codebase to be used on different target platforms. The shared code can be imported as a dependency into the native projects for iOS and Android (and other target platforms like web) and can include parts or all the invisible parts of the app: network communication, caching, business logic and more can be easily shared between your apps. All that is left to do is to add a native UI for your apps, which thanks to the modern native UI frameworks Jetpack Compose and SwiftUI is less effort than ever. In fact, even UI elements can soon be shared with Compose Multiplatform.
In our experience, based on many successfully launched apps for multiple of our customers, all code that is not related to the UI displayed to the user can be shared without losing any of the benefits of native app development. The performance of KMP is of course at native level, because Kotlin is compiled into native code. Everything the user sees and touches is 100% native, resulting in the perfect native app experience – but with all the benefits of cross-platform solutions for our development process and client.
There is a wide range of KMP libraries that can be used to accelerate development and the number of community-created libraries is constantly growing. KMP can still make use of native features with ease: Kotlin code can call all native functions provided by the operating system.
In summary, KMP offers all benefits of native app development while also reducing the associated effort due to most of the code base being shared between the platforms. With the UI still being natively developed, a KMP app will look and feel like a fully native app to the end-user. While we mostly focus on Android and iOS in this article, Kotlin Multiplatform also allows native macOS apps, Java based Windows programs and web applications to be developed.
A unique ability of KMP is to transform existing native apps into cross-platform projects long after they started. A well-maintained Android codebase can form the base for a shared KMP project as native Android functionality can be extracted to create a shared code base. We were able to migrate our biggest app project from two native apps into a KMP app with a large, shared code base. We removed over 40% of code from the iOS projects while gaining additional acceleration for future developments. Many new features or changes now come to iOS “for free” as only the KMP dependency needs to be updated. As native teams already have the skillset needed to write KMP code, this can be a gradual process over multiple months without disrupting the active development work.
But what if we want to tear down the last wall between the platforms and share (parts) of our UI? This is the promise of Compose Multiplatform, once it is available for all platforms. Like Flutter, you can write one shared UI for all platforms based on what Android developers are familiar with. The APIs offered in Compose Multiplatform are almost identical with Android’s native UI framework Jetpack Compose.
While you will lose the benefits of a native UI on iOS when making use of Compose Multiplatform, Android users will see no difference. But sharing your UI is not a black or white decision. Many elements in the UI are identical between platforms: for example, cells or list items often are supposed to look identical. Instead of sharing the entire UI, you can also share smaller elements in the UI and embed them into the native UI of your app. When done right, no difference is visible to the user, but you expanded the reach of your shared codebase even further.
Compose Multiplatform is still in beta for iOS and in alpha for web, but you can already make use of it in your Android and desktop apps.
Summary
Kotlin Multiplatform combines the benefits of native app development and cross-platform development. Instead of choosing one or the other, you can decide on your own how much cross-platform or native you want to have in your project and can tailor the split to your requirements. This flexibility makes KMP the perfect solution in many cases. For now, the UI stays fully native, but in the future, you can also choose to share parts or the entire UI between your apps.