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
Not all features in the native platforms will be made available through the framework. Some of it might be available later, but then this incurs additional delay to development when using the framework. Besides, as the native platforms continue to deliver new functionalities, the framework will always lag behind. Why Go Native if Possible
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
When cross-platform makes sense:
Source: The Fundamental Problems with cross-platform frameworks
Overview of cross-platform approaches:
- The Various Ways of Combining iOS and Android Development Together
- Why multi-platform is hard and what you can do about it
- Mobile Multiplatform and Me
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 helps Javascript developers leverage their skills into mobile development. React Native uses platform-specific native UI elements to build cross-platform apps. The original architecture has a performance botteneck in the Bridge between Javascript and native API, requires data transfer to be serialized in JSON, and has potential synchronization issue between the Javascript and native modules. Its new architecture (announced June 2018, released with version 0.68 in March 2022) replaces the Bridge with JavasScript Interface that allows JavaScript and C++ objects to reference each other, allowing each to directly invoke methods in the other, thereby removing the performance bottleneck of the old architecture. With version 0.72.0 (Apr. 5, 2023), React Native added caching to text rendering and release a benchmarks app comparing the performance of the old and new architecture. On Android, the new architecture shows 0-8% performance improvements. On iOS, the new architecture shows 13-39% performance improvements.
Articles below discuss why React Native has not panned out for its adoptee companies beyond the performance issue:
- 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
- Why React Native needs C++
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 using the Skia 2D graphics library. A Flutter app is written in the statically-typed Dart language. The biggest performance issue with Flutter is animation jank, which is especially visible on iOS. Animation jank is caused by Skia having to compile shaders at runtime, rendering it particularly visible on the first run of an animation (see the “Preview: iOS shader compilation improvements” section of What’s new in Flutter 2.2). On August 20, 2022, Flutter announced, along with Flutter 3.3, Impeller, a new rendering engine that pre-compiles shaders and utilizes modern graphics APIs (Metal and, eventually, Vulkan). The first-adopter preview for iOS was made available with the announcement, though Impeller on Android isn’t yet ready for preview as of Flutter 3.7. In addition, Dart is automating bridging to native code/SDKs with FFIgen, for languages following the C calling convention (C/C++, Rust, Go, Swift and Objective-C), and JNIgen, for Java Native Interface binding.
- Multi vs Cross Platform in the age of Flutter
- Why Flutter Isn’t the Next Big Thing
- [From Android dev] Flutter looks good, but is painful
- What’s wrong with Flutter?
- The other side of flutter/dart
- I Am Falling Out Of Love With Flutter
- On the Road to Flutter 4 and Material Design 3
React Native or Flutter?
With the performance bottleneck of React Native and the animation jank of Flutter resolved, the choice between React Native and Flutter for cross-platform development boils down to:
- to code in Javascript (Typescript as of React Native 0.71) with the JSX extension or Dart, and
- to build using native UI elements for native look and feel, resulting in non-uniform user experiences across platforms; or to build using native-looking Flutter 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.
With both Flutter and React Native, the native methods to access the device directly, such as for the sensors, must still be developed natively, in the platform languages.
As of the time of writing, I have not run across any actual performance comparison between the new React Native and Flutter along the lines of these articles:
- Flutter vs React Native vs Native: Deep Performance Comparison
- Flutter vs Native vs React-Native: Examining performance
Progressive Web Apps
- What makes a good Progressive Web App
- The state of PWA end of 2022 with more activities in 2021 and 2020
- Microsoft is Finally Ditching Electron
Kotlin Multiplatform Mobile
Kotlin Multiplatform Mobile is for multi-platform, not cross-platform, code sharing. It allows for sharing of domain/business-logic code while keeping code to access the physical device (including the screen, for UI) separate and native. KMM helps Android developers venturing into iOS development maintain a common code base. Google itself recently released a multiplatform version of Jetpack Collections (implementing Java collections API) and DataStore.
Source: Kanivets
KMM 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:
- Current [3/26/23] 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
Additional frictions on the iOS side of KMM deployments have been reported by multiple sources:
- 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).
- How Android developers write their APIs differs from how Swift developers do it.
- Obfuscated stack traces, making debugging hard.
- 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
Compose Multiplatform
While KMM is for sharing domain logic, Compose Multiplatform (CM) allows sharing of declarative, reactive UI. It’s built off Google’s Jetpack Compose with UI rendering done using the same Skia 2D graphics library powering Flutter (Skiko). For the Web KMM can also use DOM syntax (no code sharing then?). Compose for iOS is now alpha (Apr 2023) but does not yet provide built-in support for requesting runtime permissions or tracking geolocation and biometric authentication (still doable, but not production ready?). Further, in SwiftUI vs. Jetpack Compose by an Android Engineer, the author argues that, “It is important to respect UI/UX guidelines of the platform and Compose can’t build an application like SwiftUI does.”
Multiplatform development relies in large part on managing the build processes of codes for multiple platforms. This is true in the case of both KMM and CM. For the course, do we want to focus on finagling the project build process or do we learn native platform development, which both KMM and CM (and Flutter and React Native) also depend on?
Prepared for EECS 441 by Sugih Jamin | Last updated: May 14th, 2023 |