[ comments ]
I’m sick of it. In the world of mobile development (though likely also elsewhere), we sink so much engineering talent into making tools that are great within-ecosystem, but as we don’t bother making them available to other ecosystems, everyone ends up reinventing the wheel.
The end result is that we stretch ourselves thin and waste time that could have been spent innovating new things.
Titans don’t want to share
Here I admit to donning my tinfoil hat, but so often, the problem starts with Big Tech creating walled gardens. For all the decades that Apple and Google have had to collaborate, how much code is actually shared between iOS and Android, besides their common Unix ancestry? Although both are platforms for handheld devices, with all the same needs, at the level facing the app developer, they have:
- different UI toolkits
- different layout engines
- different runtime languages
- different SDKs
- different app stores
… so to deploy a native app to both platforms, we need to learn and write everything twice. It’s an insane amount of work, hence the rise of cross-platform solutions such as React Native and Flutter.
Cross-platform ≠ salvation
They’re both compelling frameworks. Flutter casts aside the disparate platforms and invents a new, consistent one; while React Native provides a single abstraction to tame all their differences. I’ve used both, and would happily recommend them for their better-than-native developer experience, even should one only need to target a single platform.
But while they do solve the “learn twice, write twice” problem, they’re still closed ecosystems. Flutter locks you into Dart, and React Native locks you into React. Neither can use each other’s native modules, and even the communities on Twitter mostly chat within their own circles.
I’ve focused on these two big names, but all frameworks are guilty. Electron, Capacitor, Tauri, Xamarin, Qt, NativeScript, the lot. Can’t we find some common ground and start building together instead of purely focusing within-ecosystem and making the hundredth cross-platform accelerometer plugin?
Glimmers of hope
I’ve been heartened by this kind of work (excuse my bias in examples—I can’t follow all ecosystems!):
- Skia is a valuable building block, being the 2D graphics library used in Chrome, Firefox, and Flutter. It’s been brought to React Native and used to implement the Web’s Canvas API in NativeScript.
- React Native Gesture Handler is a declarative abstraction for gesture-handling in iOS and Android, and has also been ported to NativeScript.
… But this is merely porting code. It’s clear that, in each of these cases, a lot of effort was needed to adapt the code (as it was written for a different ecosystem) and ongoing maintenance will remain a burden.
What I’d really rather see more of is standalone platform-agnostic modules:
- Yoga, the standalone flexbox layout engine used in React Native, has bindings for various languages, allowing it to be used just about anywhere (indeed, in NodeGUI and NativeScript). Though I’m even more excited for taffy, which aims to implement even more web layouts (and is already being ported to NativeScript!).
- JSI is another masterpiece of engineering out of React Native—a single common interface for interop between JS and native code. It’s been presented as a solution for accessing native code synchronously in React Native, but it could equally be used in (or as the core of) any JS<->native framework, as it’s an FFI. As a case in point, it is soon to be integrated into the NativeScript iOS runtime to help with layout code.
There was actually some movement in this direction in the past. Expo Unimodules was a standard to allow writing a cross-platform, ecosystem-agnostic native module. Alongside the original adapter for React Native, proof-of-concepts were made for Flutter and NativeScript (by yours truly!), but it ultimately never took off and the effort was sunset in 2021 as a result.
The lack of interest in Unimodules taught me that it’s not enough to come up with an opt-in common standard. Native modules are hard enough to write for one’s own platform, let alone for others.
Given it’s far too late to convince each framework to redesign their native module interfaces from the ground up for compatibility, it may be worth exploring a top-down approach instead. That is, producing tools to translate modules from one ecosystem to another “whether they like it or not”. We proved this with Open Native, which reverse-engineered the whole React Native native module format to allow other ecosystems to use them. It has proved its worth already by allowing several developers to reuse packages such as react-native-auth0.
What about the Web?
The Web sets a great example in openness and reuse of work:
- Web apps can consume modules from Node.js and native ecosystems (via WASM) relatively painlessly.
- The Web follows an open standards process.
- Users can extend browser functionality (e.g. through Web Extensions and user style sheets).
- Dev tools (like Prettier, eslint, and Webpack) aren’t tied to any particular ecosystem.
- There are many options for programming languages (compile-to-JS, or WASM).
- Framework authors take note of rival design patterns (e.g. JSX and Signals appear across multiple frameworks).
- No gatekeeping by app stores.
… I could go on. So why can’t native platforms take a bit more inspiration from the Web? Sure, we see pieces like HMR, JS, CSS, and flexbox here and there (e.g. React Native, NodeGUI, NativeScript), and even WebView-based frameworks that can reuse all the same technologies (it’s a start!), but why not the spirit of the Web as well?
How we could do better
Cross-community collaboration could be improved by adhering to a few principles:
- Minimise vendor lock-in. You’ll only get cross-pollination if you make it easy to come and go.
- Check for prior art first. The problem may have been solved before, and it’s worth joining forces on existing efforts in any case.
- Be open-minded to other approaches. There are great ideas in all communities.
- Listen to the little guys. They’re the ones you’re building for!
- Decouple and share core modules. Anything from your UI logic to your bridging code could be handy to somebody else!
Just imagine what we could achieve if we all worked together! For my part, I’ll keep pushing for that cause with my little experiments to bring together ecosystems, like bringing the Objective C runtime to React Native and bringing the web platform to NativeScript.
And with any luck, see you in the next issue where again, I’ll talk about Whatever.
You just read issue #2 of Whatever, Jamie. You can also browse the full archives of this newsletter.
[ comments ]