Mobile Code Sharing Strategies at Khan Academy: a Comparative Analysis

Responsible for this document: Andy Matuschak

Reimplementing the same thing multiple times is error-prone, time-consuming, and demoralizing. But the cure might be worse than the disease. Let’s take a look. No recommendations here yet: let’s discuss first.

The Android app is split into two primary modules: “core,” which is isolated from Android and implements things like persistence, networking, and business logic; and “app,” which knows about Android and implements the UI and app-level concerns. This split is a useful one for thinking about code sharing strategies: it might be useful to share only one side, or to use different strategies for each.

I’m assuming here that any transition would occur incrementally over time.

This document primarily concerns sharing code between iOS and Android but contains some discussion of sharing with the web client.

Solutions considered but cut before this stage:

Titanium, Cordova, PhoneGap, Go, Om, Fuse, Emscripten, Codename One, Ionic, other JVM-based platforms

Sharing “Core”

Separate Implementations

C++

Swift

Java[a][b]

JS via React Native (with Flow)

C# via Xamarin

  • Lowest existential risk
  • Simplest technically
  • No new skills required from team
  • No need to learn and support an extra toolchain
  • No need to write any bindings
  • “Closest to metal” gives flexibility when we need it
  • Lowest existential risk
  • Very efficient
  • Solid compile-time guarantees
  • Moderately-sized community using C++ for code sharing
  • Djinni can generate bindings
  • We already have Swift expertise
  • Very efficient
  • Great compile-time guarantees
  • We would only have to write bindings on Android
  • Our Swift/Java/C++/JS polyglots all prefer Swift as a language
  • We already have Java expertise
  • Moderately efficient
  • Decent compile-time guarantees
  • With j2objc, we would only have to write bindings on iOS
  • IntelliJ’s refactoring and analysis tools significantly accelerate us
  • Google’s doing this themselves
  • We already have JS expertise
  • If we use react-native for the app layer, avoids bindings altogether
  • Live reloading speeds iteration
  • Can deliver over-the-air updates
  • Facebook’s doing this themselves
  • The only option which allows us to share code with web client
  • Would bring dev team together
  • Established commercial solution
  • Moderately efficient
  • Decent compile-time guarantees
  • C# is a pleasant language
  • Commercial products have used it
  • There’s a reasonably-sized community around it

  • Implement everything twice
  • Team fragmentation
  • More code to maintain + break
  • Team must learn a new language
  • Net increase in number of languages in use at KA
  • C++ is both complex and hard
  • We’d have to bridge Djinni’s output to Swift from Obj-C[c][d][e]
  • Debugging tricky on Android[f][g]
  • Impossible until it’s open-sourced
  • We’ll probably have to build the Java-side toolchain ourselves
  • Totally unproven in this context
  • JNI is by far the least pleasant binding layer of this set
  • Much less expressive
  • Transpilation introduces more complexity than FFI
  • We’d be utterly dependent on this complex j2objc toolchain; dead if Google dropped support
  • We’d have to bridge j2objc’s output to Swift
  • “Core”-like functionality is not react-native’s primary focus[h]
  • Much less compile-time safety[i]
  • JS is haphazardly designed
  • Least efficient option
  • JS tooling is constantly in flux
  • Fairly unproven in this context
  • Totally unsupported by both original platform vendors
  • Need bindings on both platforms
  • We’d be totally dependent on a niche proprietary platform; highest possible existential risk[j]
  • Rough incremental adoption story
  • Team must learn a new language
  • Net increase in number of languages in use at KA
  • Totally unsupported by both original platform vendors
  • Expensive, unless they donate[k]

Sharing “App”

Not all of the strategies mentioned above are also applicable to the app layer. These lists are cumulative with the lists above.

Separate Implementations

JS via React Native (with Flow)

C# via Xamarin

  • Can use platform-specific UI debugging and authoring tools
  • Can quickly adapt to OS updates introducing new app-level behaviors
  • React Native is particularly focused on this use case.
  • React’s design for the UI layer is better than either platform’s
  • Can share a style guide with mobile web
  • Our organization already knows how to build React UIs
  • Has some nice multi-platform UI debugging and testing tools
  • There’s a cross-platform UI abstraction API, Xamarin Forms, which gets mixed reviews

  • We don’t love either platform’s app-level APIs
  • Animation and gestures are still somewhat open API design domains
  • No 0-day support for new OS features (pretty easy to bind ourselves)
  • None of us has experience with the concepts in the abstraction layer
  • No 0-day support for new OS features (harder to bind ourselves)

[a]Did you consider testing the Intel Multi-OS Engine to use Java code on iOS: https://software.intel.com/en-us/multi-os-engine

Full Disclosure: I am the CTO of Migeran, Intel bought our "Migeran for iOS" product and it is now part of the Multi-OS Engine, we are Intel Support & Development Partners.

[b]Nope, but in general we’d avoid pre-release solutions.

[c]Why? The generated Objective-c code is swift friendly, with annotations

[d]Objective-C-bridged code isn't Swifty. Especially if we shared our core components, many of those interfaces would want to be expressed using value types.

[e]Good point, thanks :)

[f]Supposedly not anymore since the latest Android Studio.

[g]oooOOOoo

[h]There are quite a few JS libraries and frameworks targeting persistence and networking (ranging from facebook's relay and netflix's falcor to HTTP libraries like fetch polyfill), but understanding the JS ecosystem takes quite a bit of effort :)

[i]At v1.8, typescript works fairly well for compile-time safety. It has more expressive type system and tools compared to both Java and Swift (such as union & intersection types, type alias and destructuring which reduce code verbosity).

[j]Xamarin is now open source

[k]Now it's cheaper