Native-only development

TL;DR

Three main reasons:

If you think it is a good idea to use Jetpack Compose (or any other multi-platform UI library like Flutter or React Native) to build an Android and iOS application, it is probably because you’ve never developed or used an iOS application before . . . . It is important to respect UI/UX guidelines of the platform and Compose can’t build an application like SwiftUI does. SwiftUI vs. Jetpack Compose by an Android Engineer

Native applications receive updates to their software development kits (SDKs) faster than cross-platform frameworks. This means the latter can face potential delays in incorporating new functionalities or taking advantage of the latest platform enhancements. Native vs. Cross-Platform Development

Shared code . . . can lead to performance issues, lack of native look and feel, and complications with debugging and tooling. Slack’s mobile developers wanted to work with native language features and APIs, and build best-of-breed apps for each platform . . . . Stabilize, Modularize, Modernize: Scaling Slack’s Mobile Codebases

Other reasons (privacy, cost): Making Ollie: Creating a local iOS app in a time of React Native.

When cross-platform makes sense:


Source: The Fundamental Problems with cross-platform frameworks

Overview of cross-platform approaches:

Also, except for PWA (progressive webapp), to develop a cross-platform app for iOS still requires use of Xcode and macOS.

Below are additional articles on why various cross-platform solutions have not panned out for their adoptee companies.

React Native

React Native allows Javascript developers to leverage their skills to develop for mobile. React Native uses platform-specific native UI elements to build cross-platform apps. The original React Native architecture had performance issues interfacing Javascript with native APIs. To address this issue, a New Architecture with JavasScript Interface (JSI) was introduced with React Native 0.68. The benchmark app released with React Native 0.72.0 showed that the new architecture has a 0-8% performance improvement on Android and a 13-39% performance improvement on iOS and is enabled by default as of React Native 0.76.

Articles below discuss why React Native has not panned out for its adoptee companies beyond the performance issue:

Flutter

Flutter is a cross-platform UI toolkit embedded in an “ambient” native app, allowing applications to interface directly with underlying platform servicess. Instead of using native UI elements (widgets), Flutter implements its own, customizing them to mimic the UI elements of each platform, which it then renders to the screen. Flutter originally rendered using the Skia 2D graphics library, but due to animation jank issue, especially visible on iOS, Flutter introduced a new rendering engine, Impeller, which pre-compiles shaders and utilizes modern graphics APIs (Metal and Vulkan). The Impeller adopter preview was released for iOS in Aug. 2020 (Flutter 3.3) and became the default in May 2023 (Flutter 3.10). The Android adopter preview was released in Nov. 2023 (Flutter 3.16) and became the default in Dec. 2024 (Flutter 3.27). For more technical discussions on the Impeller rendering engine, see A deep-dive into Flutter’s Rendering Engine and Flutter developer docs.

A Flutter app is written in the statically-typed Dart language. Dart automates bridging to native code/SDKs for languages following the C calling convention (C/C++, Rust, Go, Swift and Objective-C) using FFIgen and with JNIgen for Java Native Interface binding.

Flutter was forked into a community-maintained Flock project (10/27/24 ), heralding a new era of responsive evolution or a harbinger of decline, disuse, and dissolution?

Multiplatorm frameworks: Squid and KMP+CMM

Squid transpiles Swift/SwiftUI code to Kotlin/Compose, free to individuals but not to educational institutions.

Kotlin Multiplatform is a multi-platform, not cross-platform, solution. KMP (née KMM, Kotlin Multiplatform Mobile) helps Android developers venturing into iOS development maintain a common domain/business-logic code base (in Kotlin), while keeping code to access the physical device, including the screen, separate and native.

Source: Kanivets

Compose Multiplatform (CM), a Jetbrains-maintained library built off Google’s Jetpack Compose, allows sharing of declarative, reactive UI code. In addition to Compose, Google has also released a multiplatform version of Jetpack Collections (implementing Java collections API) and DataStore. Compose Multiplatform, combined with KMP and the rest of the Google multiplatform libraries, is close to providing a cross-platform solution. Whereas React Native apps build with native UI elements and Flutter widgets mimic those of the native UI elements, Compose Multiplatform employs the Google Material Design language across all platforms.

UI rendering on Compose Multiplatform is done using the Skia 2D graphics library (Skiko on iOS), which originally powered Flutter, but which Flutter is replacing due to its startup animation jank. To SwiftUI, a Compose UI View is treated as a UIKit View. SwiftUI state managment must then be manually conveyed to a ComposeUIViewController as with using any legacy UIKit View (see Compose Multiplatform —- managing UI State on iOS). Compose for iOS is now in alpha, usable but not yet production ready. As for the use of Google Material Design language, “It is important to respect UI/UX guidelines of the platform and Compose can’t build an application like SwiftUI does.” – SwiftUI vs. Jetpack Compose by an Android Engineer.

On iOS, Kotlin/Native compiles Kotlin into LLVM backend, same as native iOS. It is thus “native” from the platform application binary interface (ABI) level:

The integration burden to be iOS/Swift aware is also born on the Android/Kotlin side:

Dynamic memory management on iOS is done at compile time, with Automatic Resource Counting. Kotlin uses runtime garbage collector to manage dyanmic memory allocations. The interoperation between them introduces memory overhead, pitfalls, and other performance considerations.

Additional reported iOS-side frictions:

KMP+CM is most useful in an enterprise environment, where the investment in project build process could pay off over time and across projects, and on apps where the business logic largely resides on the front end with heavy client-side persistence and offline support:

Multiplatform development relies in large part on managing the build process, to juggle and interface code between platforms. This is true for both KMP and CM. While JetBrains is striving to automate the build setup with Kotlin Multiplatform Wizard and to make the multilingual development experience itself more integral by introducting Fleet, a “polyglot IDE”, still, for this course, do we want to focus on finagling complex build processes or do we learn native platform development, which all cross-platform, multi-platform frameworks in the end fundamentally depend on when it comes to accessing the device? (See Convert Your Native Project to Kotlin Multiplatform – Developer’s Guide. For “complex dependency management” of KMP over Flutter, see [Kotlin Multiplatform: The Future of Cross-Platform?] and Is Kotlin Multiplatform Mobile Replacing Flutter?, the latter also pointed out that Flutter has “slightly limited access to very specific native functionalities.”)

Cross-platform frameworks comparison

With the performance bottleneck of React Native and the animation jank of Flutter resolved, the choice between React Native, Flutter, or Kotlin+Compose Multiplatform for cross-platform development boils down to:

  1. language choice:
    • Javascript (or Typescript as of React Native 0.71), with the JSX extension,
    • Dart, or
    • Kotlin. Kotlin+Compose multiplatform currently presents a larger overhead in project setup and a less mature path in multiplatform integration, especially disadvantaging the iOS side.
  2. UI/UX look and feel:
    • React Native: to build using native UI elements for native look and feel, resulting in non-uniform user experiences across platforms,
    • Flutter: to build using native-looking UI elements (widgets) for uniform look and feel across platforms, but potentially non-native experiences on particular platform, such as the use of non-native navigation drawer (hamburger icon) on iOS,
    • Kotlin+Compose multiplatform: Material Design look and feel. One can, however, use KMP without CM for the flexibility of building UI natively using SwiftUI, at the cost of even higher project build management overhead.

With all three, methods to access the device directly, for example device sensors, must still be developed in the native platform languages.

Progressive Web Apps


Prepared for EECS 441 by Sugih Jamin Last updated: December 12th, 2024