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:
- When to Go Native: Astro’s Journey from React Native to Native
- Snipe.gg’s experience with React Native
- Udacity’s experience with React Native
- Airbnb’s experience with React Native
- React Native Dilemma
- Facebook Messenger
- Downsides of React Native
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.
- Multi vs Cross Platform in the age of Flutter
- [From Android dev] Flutter looks good, but is painful
- The other side of flutter/dart
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:
- What’s Native? Nevertheless, by targetting low-level Objective-C and UIKit compatiblity, KMM does not fit smoothly with Swift/SwiftUI development:
- First Impressions Integrating KMM into a Brownfield Project:
- KMM only interops with Objective-C, so an understanding of Objective-C is required to utilise KMM proficiently. KMM generates a header file exposed to Swift, which means we effectively call Objective-C code from our Swift code. This comes with its own set of limitations. For example, if we want to utilise default function parameters in our shared code, it won’t translate to Objective-C, because Objective-C doesn’t support default function parameters.
- You should be aware that while Kotlin and Swift are similar, there are still language limitations, and things you use daily in Swift might not be available in Kotlin (and vice-versa), see also, Kotlin Multiplatform – How to improve the iOS development experience.
- How Android developers write their APIs differs from how Swift developers do it.
- Obfuscated stack traces, making debugging hard.
The integration burden to be iOS/Swift aware is also born on the Android/Kotlin side:
- JetBrains maintains an official Kotlin-Swift Interopedia to document “workarounds and library solutions” for Kotlin language features that cannot be easily called from Swift. “To provide the best possible experience for Swift developers, the rule of thumb is that using the simplest [Kotlin] language features is best.”
- For some of the not-so-simple language features such as abstract data sum type (enums), generics, coroutines, and asynchronous sequences (flows), André Oriani has written a ten-part series on Writing Swift-friendly Kotlin Multiplatform APIs.
- There are also other efforts to mask platform specific differences, for example: How to avoid asking for Android Context in Kotlin Multiplatform libraries API
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:
- Kotlin Multiplatform: A Multi-Platform for Native Developers
- KMP, iOS Developers and Production: Learn How Swift Developers use KMP to Develop Multi-Platform Apps
- Mastering the Mobile Dev Maze: UIKit vs. SwiftUI vs. XML vs. Compose — Part 3 see “Comparison of Predictions and Outcomes: iOS”
- App size and startup time: native, KMP/CM, Flutter
- We tested out Kotlin Multiplatform Mobile (KMM)
- Different Approaches in Consuming KMM Modules in iOS
- The Precarious Problem of Kotlin Multiplatform on iOS
- Three-framework problem with Kotlin Multiplatform Mobile
- We asked an iOS developer his thoughts after working with Kotlin/Multiplatform Mobile “So is Kotlin multiplatform wonderful to use? I’m not going to sugarcoat it for you. It kinda sucks . . . the current tooling for multiplatform is painful. If you want to use bleeding-edge tech, expect to bleed.”
- Create a Kotlin/Multiplatform library with Swift
- Some Errors I’ve Found Developing with KMM
- Navigating seas with KMM – an iOS Developer story mostly Kotlin vs. Swift incompatibilities.
- How we moved to KMP at Deezer: Limitation iOS
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:
- Compose Multiplatform – hot or not “There is also a case of using libraries provided by a third party that are solving key issues for KMM. What if that crucial library stops being supported? It would be safer if you’ll create those solutions on your own, right? In that case it would take time. Time and money that you wanted to save by going multiplatform.”
- Current State of Kotlin Multiplatform Mobile
- An Opinionated View on KMM
- Key takeaways from our Kotlin Multiplatform journey
- Kotlin Multiplatform Mobile (KMM) at Granular
- Adopting Kotlin Multiplatform Mobile (KMM) on 9GAG App
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:
- 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.
- 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
- What makes a good Progressive Web App
- Overview of Progressive Web Apps (PWAs)
- Are Progressive Web Apps the future?
- What’s New in PWAs for 2025?
- What PWA Can Do Today
Prepared for EECS 441 by Sugih Jamin | Last updated: December 12th, 2024 |