· 9 min read Posted by Kevin Galligan

Compose Multiplatform and the Native App Future

Fully native mobile apps was for years the default mobile app development option. As cross-platform solutions and device capability have improved, “good enough” has become the default choice for anything that isn’t a big, public app. Kotlin Multiplatform and Compose, which can seamlessly blend shared and native UI, can make “native” the default choice again.

Overview

Virtually everybody producing mobile apps would agree that a polished, native app experience is the preferred choice. However, it is impossible to deny that writing two separate apps is inefficient. You’re building almost the same thing twice. With different teams.

For years, startups and digital products who needed mobile and did not want to risk a bad app experience would choose native. The risk of cross-platform is that if the quality of the end result isn’t sufficient, besides lots of duct tape and compromise, you’ll need to rebuild them from scratch.

Over time, the cross-platform options have improved considerably. While the result is generally not as “polished” as a fully native app, the result is rarely so bad that a rewrite is necessary.

Cross-platform became less risky. “Good enough” has indeed become good enough.

Most native app development has consolidated to spaces where performance and UX are so critical that the relative cost of maintaining two apps, and two app teams, is much less than risking user adoption and experience.

In summary, native at one point was the default “smart” option. That is no longer true.

KMP and Compose Multiplatform are a game-changing option. A “cross-platform” mobile app, which can blend fully-native UX, wherever you want it. The risk of choosing “cross-platform” is not just reduced. It is effectively eliminated. A fully shared-ui might be good enough, but if it’s not, you can add as much native UI as you need, without a rewrite.

Cross-platform efficiency, native experience.

Livestream

One of the Touchlab workshops this year at KotlinConf demonstrated how to blend multiplatform Compose and native iOS UI, with shared underlying KMP architecture. We’ve recently presented this content, so you can start exploring how all of this works from a technical perspective.

This presentation is not a comprehensive look at how you would do this at scale. While the underlying tech is quite functional, awareness of the possibilities KMP and blended-native UI both enable is just beginning to be understood by the wider community. Many libraries and patterns need to emerge, and that mostly comes from the community. Even at Touchlab, were this presentation written today as opposed to a few months ago, we would probably use some newer internal libraries that have since been built, and include some emerging community libraries that exist to help reduce boilerplate.

In summary, the community at large should hear about what’s possible. There are many useful libraries and tools that could be built, and for the members of the community looking to be involved in open source, this is an amazing time to get started.

Work With Touchlab

Is Touchlab building and shipping apps with Compose Multiplatform? Yes we are. Can we help build yours? Yes we can. Let’s talk.

Conceptual Context

There are a few key concepts that need to be explained to understand why KMP and Compose has the opportunity to be a true game-changer for mobile. The technology isn’t simply “another option”.

Blended UI

The capability to blend UI is interesting, but the implications are important to understand.

First of all, in terms of “native” vs “cross-platform”, it is important to remember that “cross-platform” only applies to iOS. On android, Kotlin and Compose are the recommended technologies to use for native development.

There is no “cross-platform” on Android with Kotlin and Compose.

On iOS, the “blending” of UI is inline. A SwiftUI or UIKit view inside of a Compose screen is not an overlay, as it would be with Flutter. It is rendered inline. The same is true going the other way. If you built your app with native iOS as the “host”, you can include Compose as portions of the screen, and they live as actual inline portions of the screen.

Inside a Compose app on iOS, if you display a SwiftUI view, it does not live in an entirely different architectural world. The iOS-native UI can directly leverage the KMP code and architecture that is driving the equivalent Compose view on Android.

The shared architecture is a critical point. You can code your screens/features with Compose, then if you decide something should be native, you can simply make that portion of the Compose into an expect/actual function, and render the iOS-side with a native view. Pass in the same KMP data. The iOS view can render that data, and communicate events back into the underlying KMP-based code.

You can quite literally replace Compose with SwiftUI or UIKit, with the same data. That doesn’t need to be the full screen. It can be just a defined area. In some cases, you need to do this. The livestream has an example with a map view. You do not need to wait for Google to release a CMP map view, nor do you need to sort out clunky overlays.

80/20 UI

In most apps, users interact with a small portion of the app most of the time. Say a home feed, vs a detail screen, vs something deep in the settings. The “80/20” that we use for the name of this concept is not derived from some industry average. It is in reference to the 80/20 rule. It represents a general imbalance that manifests itself, well, all over the place. Way beyond mobile dev.

Specifically for our blended UI, the idea is that your users spend virtually all of their time on a few core screens. In many apps, there are settings screens and other support views that many users may never see. The experience of the user does not reflect the range of views that the team needs to build.

From a product development perspective, that means you can build the entire app with a single UI and architecture, then spend as much time and effort as you like to write native views in iOS. Presumably, that effort would be focused on the screens your users are most likely to spend time on.

Keeping in mind that Android is always native, and that your iOS users will spend the majority of their time on core screens, this is the practical result. An almost entirely native experience for your users, with almost a single codebase. The degree to which that experience is native, and the code is shared, is flexible, and entirely within the purview of the people making product decisions.

It can be as native as you want it to be.

There are further implications to this that would be best left to separate posts. One quick highlight is product development efficiency. You can develop and iterate a new feature entirely in Compose, and once those iterations settle, apply a native UI for iOS. For new products, with significant iteration, this reduces the overall work and effort considerably. Product iteration involves “wrong paths”. Work is thrown away. That is not waste. It is the cost of fine-tuning a product. Lessons learned. However, if you are building for two separate platforms, and could learn those lessons on one, then the effort for the second platform is waste. You did not need to do that work, and gained nothing from it.

Risk Elimination

One of the biggest hills that shared-ui, cross-platform tools have had to climb is their inherent risk cliff. You need to make a Big Decision up front about the platform. If that ultimately turned out to be a bad decision, you would not really know until late in development, or possibly after release. At that point, you have a few options. None of them good. While a cross-platform implementation may indeed have been perfectly fine, the risk involved with a bad decision kept many teams building native apps.

As cross-platform tools improved, and as device capabilities improved, the “common consensus” is that a cross-platform app is unlikely to be so bad that you need to rewrite it. The risk cliff is still there, but the likelihood that you’ll be on the wrong side of it has diminished.

KMP and CMP are designed to integrate flexibly with the platform they run on. That means, if you build an app entirely with Compose, and you later decide that you would be better off with a much more, or entirely, native iOS UI, you are not faced with a rewrite. You can choose how much of the iOS app should have a native UI. In addition, you have a fully functional model on Android, with shared architecture, from which to build the iOS UI.

I would not say “zero risk”, but the cliff is an order of magnitude less. It is a fundamentally a different situation.

This is one of the major reasons why, as the CMP ecosystem matures, it won’t just compete with other cross-platform options. It can be the default. Why would you choose the risk cliff if you didn’t need to?

As a final point, you can add native iOS UI to your app. I suspect many apps won’t. Why? For the same reason teams choose Flutter or React Native now. It’s good enough. The difference is, you can mix native UI. The fact that you can dramatically lowers the risk of choosing CMP vs native or other options.

Risk should have a return. If it does not, choosing risk seems like a pretty bad choice.

Other Topics

There are many, but covering them all here would result in a rather long post. Some examples:

  • The critical role of the Android/Kotlin community
  • Web and WASM, as WASM-GC becomes the default
  • Timeline of platform and tooling maturity
  • Practical use-case considerations for various options
  • Applying these tools to existing “legacy” applications (without rewriting them from the ground up)

Kotlin and Compose Multiplatform are amazing technologies. In the “cross-platform” space, they have built upon, and taken lessons learned, from many previous technologies.

To my native developer friends, yes, every cross-platform framework has been relatively disappointing. If KMP hadn’t learned from these lessons, it would be just another option. It has learned from those lessons.

To anybody in the “decision-making layer” of a product or org, you probably haven’t heard a lot about this tech, but you will soon. Now is a good time to take a look.

If you’d like to hear more soon, I’ll add a personal plug. I’ll be giving the Keynote at Droidcon NYC this year: The Future of Android, Kotlin, and Everything. If you’re attending, come check it out.