Thursday, November 20, 2014
Editor’s note: Be forewarned that the following post has much more technical mumbo-jumbo than our normal fare, taking you behind-the-scenes of the development of Inbox. So if you’re a practicing engineer, an aspiring hacker, or just plain interested in knowing how the sausage is made (mmmm sausage), read on!
But in those same 10 years, a lot has changed. The capabilities and diversity of devices has exploded. Users expect to be able to move from a laptop to a phone and have their apps work flawlessly. As a result, developers are facing a new challenge: how to build a high-quality app across platforms, such as Android, the web, and iOS, without sacrificing quality or execution velocity. As a developer, maybe you’ve asked yourself, do you rewrite your app three times to optimize it for each platform, wringing out every last bit of performance and polish? Or do you aim to get the app to market sooner by building a web-based “hybrid” app that leverages the same technologies across platforms (but potentially sacrifices integration and user experience)?
Facing the challenge
Of course, there are a number of elements of Inbox that are shared across the three platforms: code for managing network communication, caching objects, local persistent storage, managing user edits both locally and remotely, and supporting it all while offline. This logic must be faithfully and correctly implemented and kept up to date on all three clients. Rewriting it three times in three different languages would soak up substantial engineering resources and slow down how quickly we make improvements to Inbox.
Cutting the Gordian Knot
In order to address this challenge we took a novel approach in which data model and application logic (conceptually the “Model” in “Model-View-Controller”) is written once in Java. This data model abstracts concepts unique to Inbox like Conversations, Reminders, Contacts, and Labels, and provides a fully observable data model for convenient binding to the user interface (UI) layer. We built the Inbox app for Android directly on top of this Java data model.
The plot thickens
For iOS we developed the now open source J2ObjC cross compiler to translate our Java data model to Objective-C, and again we get a natural API on which to build our native iOS Inbox app (complete with -[Reminder snooze]). The astute reader may wonder how we deal with the impedance mismatch when translating from a garbage collected language (Java) to a reference counted one (Objective-C). Generally, J2ObjC relies on Objective-C autorelease pools, so objects normally garbage-collected are instead freed when a pool drains. One problem with this approach is reference cycles; in places that cycles exist in our Java data model, we use a Java annotation to identify the @WeakReference. When transpiled, the corresponding property in Objective-C will have the __weak modifier, thus breaking the retain cycle. In practice we’ve found this to be a relatively minor problem and we have automation tests that flag the rare cases of new cycles creeping into the object model.
If you’re building an application that (a) has significant UI independent client logic, (b) is targeting multiple platforms, (c) must not compromise on user experience and polish, you now have a new option to consider: a shared, cross compiled data model powering fully native application UIs. This has worked well for Inbox, where we are sharing roughly two-thirds of our client code, and have delivered a product with the same functionality and ship date, without having to rewrite the entire thing three times. Want to learn more about the technologies that power Inbox? Check out http://gwtproject.org and http://j2objc.org.