Wikimedia Apps/Wikipedia/Hybrid notes

Current plans for the new (late 2013-early 2014) Wikipedia reader mobile app are starting to shape up; Brion is doing a little proof-of-concept work.

History

 * 'wapedia'-based HTML+WML mobile web gateway on *.mobile.wikipedia.org
 * Ruby-based HTML+WML mobile web gateway on *.m.wikipedia.org
 * old native iOS app wrapping the Ruby-based mobile web gateway, with magic User-Agent based tweaks to hide toolbar
 * MobileFrontend MediaWiki-based HTML+WML mobile web gateway on *.m.wikipedia.org
 * PhoneGap/Cordova-based HTML app for iOS, Android, BlackBerry Playbook, and later Firefox OS
 * initially scraping the web gateway
 * switched to API method that fetched the mobile web formatting in structured format
 * shared a few JS/CSS modules with MobileFrontend

Problems with Cordova app
While there were some portability benefits to the PhoneGap/Cordova-based app, native-feeling UI and system integration were difficult. Limitations in old web browser engines meant we were limited in the UI snazziness we could provide, and it was difficult to add in native modules for things like saving data offline.

More generally, adding more features to provide parity with the mobile web site would become increasingly difficult; features including UIs tend to include a mix of frontend and backend work making code sharing possibilities with the Cordova app limited. Adding authentication, editing, watchlist, diffs, registration, blah blah was going to look really hard.

(Compare with the native Commons apps which have a much more limited domain, and don't need to replicate nearly as many web features.)

Hybrid vision
Since we're doing all this UI feature work on the mobile web, and reading articles means we spend much of our time in a giant webview anyway, there's some strong appeal to building a hybrid native/web app that essentially acts as an enhanced web browser with additional app-specific native APIs.

This would enable us to wrap all mobile web features into the app, for online usage with possibly not-quite-native interfaces. Even things like the Visual Editor should work, without having to reimplement the entire editor in native code!

We can then add pure native modules to handle parts of the UI, or better file fetching for uploads, or an offline storage extension, etc.

Comparison with Cordova
In some ways, this is similar to the Cordova vision -- in fact we would likely end up using some of the same techniques for communication between the HTML/JS code in the web view and the native code around it.

However, Cordova is a bit heavyweight and difficult to manage; there's more boilerplate code which makes updating across versions difficult if you have a lot of native code, and it exposes lots of Cordova-specific APIs we'll never use, like access to contacts and a pre-Blob+XHR file upload feature. We'll save a couple megabytes on distributable size by ditching Cordova, and keep our boilerplate code simpler and more maintainable.

Operating system support

 * Currently, iOS and Android versions of the new reader app are definitely planned.
 * Firefox OS is likely -- we have an app in the store but it's very limited. Even if we add no special support, replacing the current code with a wrapper around mobile web site would add features.
 * Windows 8 is a maybe -- we already have an app in the store and may wish to roll it in to the new app program so it doesn't have to be maintained separately.
 * Windows Phone 8 is a maybe -- we don't have an app there currently, and there's not a lot of demand.
 * BlackBerry 10 / PlayBook is unlikely to get done due to lack of demand, so I haven't researched it. We have a PlayBook version of the Cordova app but will likely retire it.

A few summary notes from that:

Bridge API
We should probably try to have a stable, abstracted API between the native and JS sides. As much as possible should end up:
 * 1) common to all OSs
 * 2) backwards- and forwards-compatible as people do or don't update their apps on time
 * 3) not a clusterf**k when we refactor markup, styles, or JS on the MobileFrontend sides

Ideally, the MobileFrontend code should contain little or nothing OS-specific, allowing us to roll out new apps at our leisure.

Native to JS bridge
Most target OSs allow executing JavaScript code in the web client; this can easily be used to pass data and trigger events on the JS side, and could be pretty common to most OSs on the client side.

Firefox OS doesn't have a direct way to do this, but if using an we can have a small stub on the MobileFrontend side for handling window.postMessage from the parent frame, which can then shim into the same function calls used by the other OSs directly.

Only Android can inject objects into the JavaScript VM before page load, and this is limited to injecting Java objects with some given method interfaces. When using JS execution only, we have to wait until the client frame at least partially initializes before we can notify the client that it's running in the host app and should start communicating with its surroundings. This may have affects on what UI components we can remove from the web view.

JS to native bridge
Each OS has a slightly different way to communicate data back to the host app. It might be simplest to let each OS inject its JS-to-native bridge code via the native-to-JS bridge.

For Firefox OS, we could use a fallback to window.parent.postMessage.

Trapping external navigation
iOS and Android allow the host app to easily detect all navigation attempts in the web view and optionally redirect them to an alternate action, such as sending external links to a separate web browser application.

The JS side needs to be able to hijack navigation to external URLs itself for Firefox OS, Windows 8, and Windows Phone 8. A global generic a.onclick handler may work, then pass it off to native code.

UI modifications
Launching a custom native activity or view in response to a user action in the web client should be fairly straightforward; have the host register a handler for an event or patch a function, and have that send data over the bridge to the native side. A command can then be sent back when done, to trigger a reload or navigation to another page.
 * Example: detect tap on the 'hamburger' button, replace the showing of web menu with a native menu
 * Example: detect launch of external URL, pass it to native code to launch in external browser

Actually changing the UI inside the web view is potentially harder.
 * Example: hide the web toolbar/searchbar at top, replace it with a native ActionBar or UIToolbar etc
 * we can't easily inject a CSS class or run a JS function until after the page is displayed and loaded
 * in iOS and Android 4 we could modify the data before we load it...
 * or we could avoid showing the web view's contents until we've made our adjustments...
 * JS could detect custom user-agent, but that only gets us for iOS and Android. Firefox OS, Windows 8, and Windows Phone 8 don't let us override the user-agent at all.
 * we could force a custom URL parameter or something, but that'll get ugly and has extra caching issues