Skip to main content

Android and iPhones and Web, oh my

It's regularly asked why J2ObjC purposely avoids translating UI code; after all, wouldn't it be wonderful if a tool existed where a developer can drop in Android source and out pops an iOS app?

Our usual response is that world-class apps need user interfaces that are tightly integrated with each platform, and that common-denominator attempts to span platforms degrade user experiences. As I found when working on Swing many years ago, customers notice the smallest deviations from a platform's UI standards and generally find them off-putting. But non-compromising UIs are just one of the reasons we focus on translating shared logic.

The pressure for platform-independent apps is due to the cost and effort to create separate versions for each platform. This is especially true for the non-UI parts of the app, where the logic is identical but must be rewritten in different languages for Android, iOS, and browsers. Software development is expensive, and this sort of redundancy is wasteful, risky, and perhaps most important to developers, mind-numbingly boring. The Don’t Repeat Yourself principle is very applicable when building apps for multiple platforms.

Additionally, there are hidden costs due to differences between an app on different platforms: missing functionality, unique bugs, etc. Your marketing people will (should) argue that this weakens the app's brand; in other words, its reputation among its current and potential customers. When an app behaves differently on different platforms, it’s often perceived as being lower-quality than its competitors.

J2ObjC was created to help address the problems associated with independent releases, without sacrificing the requirement for world-class, platform-specific user experiences. It's goal is to provide a GWT-like translator and runtime, so iOS apps can be designed to share as much Java code as possible without compromising best-of-breed user interface designs. The key word in the previous sentence is designed, because as software engineers know, reusability needs to be an important design goal and not just an afterthought.

We’ve found that test-driven development really helps support code reuse, because there is a huge overlap between testable and portable code. If code is hard to test, it's also likely to be difficult to use on other platforms; UI and networking code in particular are difficult to unit test easily. By writing tests as the design is being implemented, this difficulty encourages hard-to-test code to be isolated from more easily tested code. Often, the design can be improved to further isolate hard-to-test code and thereby increase sharing, such as using dependency injection or mock objects in tests. Software development is challenging enough without sticking to designs that create unnecessary work.

We strongly encourage using continuous integration for apps on all platforms they target. When an app is written to share Java code using Android, GWT, and J2ObjC, any changes that are committed to that shared code cause it to be rebuilt for Android, translated to JavaScript by GWT for browsers, and translated to Objective-C by J2ObjC for iOS, and all unit tests executed. Any changes that break any of these builds can then be quickly fixed or rolled back. This infrastructure allows engineers to work together, regardless of their Android, web, or iOS experience; effectively it leverages their work, cutting in third the effort that three separate ports of an app would otherwise require. It also allows the team to assess the three platform builds as parts of a single product, with (for the app's core functionality) a single set of features and outstanding issues.

Hopefully this clarifies J2ObjC's objectives. It's why we consider it more of a compiler than a source translator, since the output that matters most is what winds up in each app build, not how pretty the intermediate Objective-C looks (though it's very useful to inspect generated code). It's why the JUnit and Mockito libraries were added before many JRE classes. And it's why we open-sourced J2ObjC; the software engineering community developed all these best practices we rely on, and as community members it's important we help improve them further.


  1. Why not just build the non UI parts in C since both platforms support C natively?

  2. This is absolutely fascinating to me. I wrote an article on multi platform UI design back in 2012, when Google's apps didn't look great on Android or iOS. I remember feeling this need for apps that function the same way on any platform, yet have a UI that feels right at home on the given platform. I'm so pleased to see the solution Google has demised. Working at a software company myself, I would want any of our mobile apps to be built the same way. Thought I'm just a user advocate on the dev team, so time for some convincing. Thanks Tom.

  3. @kdt because android C support is limited and requires JNI which is much more complex.

  4. This comment has been removed by a blog administrator.


Post a Comment

Popular posts from this blog

Breaking Retain Cycles with @Weak and Other Techniques

The runtime on iOS and macOS uses reference counting to manage memory. When two objects make strong references to each other, a retain cycle is formed. Consider a Document object that has a Header. The Document owns the Header, but often the Header is also modeled to have a document (or “parent”) field:

cfront: a J2ObjC Inspiration

Developers new to J2ObjC may find its "Java compiles to Objective-C, which compiles to .o files" approach a little strange, but it's based on precedent: cfront. When C++ was first released, no compilers translated C++ sources directly into object files. Instead, the cfront script translated C++ into temporary C files, and then invoked cc to compile them. cfront took similar options as cc, so most C++ developers used it as if it was a true C++ compiler. The cfront script wrapped around a transpiler, though; it was a script very similar to combining the j2objc and j2objcc scripts in J2ObjC.
One of cfront's innovations was name mangling, where C++ type information was embedded into C names. J2ObjC also uses name mangling (described here) to support features like packages and Java's method overloading semantics that Objective-C doesn't directly support. For example, the JRE has both java.util.Date and java.sql.Date classes; since C names are all global in scope,…