RoboVM is one of the projects I’ve been keeping a close eye on for a while now. Its aim is to allow native iOS apps to be developed in Java. With its recent 1.0 beta 2 release I thought it was time to take it for another spin.
If you ever tried maintaining an app with two separate codebases for Android and iOS at some point you probably wished for a way to share code between the two, reducing duplication and saving development time. I certainly did. And so did Google for their Inbox app apparently – they ended up writing their J2ObjC Java to Objective C transpiler.
RoboVM takes a different approach: it compiles JVM bytecode to native iOS binaries, providing effectively a JVM implementation on iOS (based on a port of the Android class library and the Boehm GC) that runs your app.
Eclipse is the primary supported IDE at this stage. The RoboVM Eclipse plugin takes care of compiling and lets you run your app in the iOS simulator or device. There are also plugins for Maven and Gradle so you can build your project from the command line, and a basic Intellij plugin (that I haven’t actually tried yet) that wraps the Gradle build.
Whilst most of RoboVM is open source (Apache licensed), there is an Eclipse debugger available as a closed source add-on. It’s currently in beta but I guess starting from the first stable 1.0 release it will be offered at a (hopefully reasonable) price to sustain the project, which is only fair enough. Stepping through Java code while running an app in the iOS simulator was a bit of a wow moment for me.
Creating User Interfaces
So far it seems that RoboVM has been used mostly – if not exclusively – to create games, many of them based on libGDX, where the graphics use the OpenGL ES API. Creating non-game applications that use native UI widgets is a different story, but RoboVM now provides bindings around the Cocoa Touch APIs to make it possible.
There is no visual designer at this stage. It is possible to use Xcode and Interface Builder to create NIB files and then import them into a RoboVM project, but it’s quite a fiddly process. It’s easier to create all the UI in code. This is not necessarily a bad thing actually: some experienced iOS developers deliberately choose the same approach anyway – see e.g. Why I Don’t Use Interface Builder.
Using Third-Party iOS Libraries
While RoboVM already provides bindings for Cocoa Touch APIs, any non-trivial app typically needs to integrate with some third-party iOS libraries, like the AdMob SDK or the Facebook SDK.
This is possible using the Bro Java to native bridge. Basically you need to create some Java classes and methods mirroring the API you need to use, declare the methods as native and add some RoboVM-specific annotations to specify how they should be mapped.
I did this with AdMob and it turned out to be pretty straightforward, considering you only need to wrap the methods you actually use in your app rather than the full API. Blue River Interactive has a large collection of RoboVM iOS Bindings – not all of them up to date with the latest versions but at least very useful as examples.
Using Third-Party Java Libraries
In theory, pretty much any existing Java library could be used with RoboVM and the few ones I tried certainly worked. Of course when targeting mobile devices you’ll want to keep dependencies to a minimum anyway.
The RoboVM compiler excludes any unused class from the output by default to reduce the binary size. Sometimes you need to explicitly declare in your project configuration that some classes need to be linked even if they do not appear to be used, typically because they are only references at runtime via Class.forName().
The trickiest case I’ve encountered is trying to enable HTTP/HTTPS support. Seems like the only approach to find out which classes are needed at runtime is trial and error, and I ended up with several entries in the forceLinkClasses section just to be able to call a web service.
I suspect it’s probably better to use native iOS functionality where possible, e.g. NSURLSession for HTTP calls. You could still create a shared HTTPClient Java interface with Android and iOS specific implementations for code reuse.
So far I’m pretty impressed with RoboVM and in the process of migrating an app to it for further validation. I’m curious to do some memory profiling and see how the garbage collector behaves. I did stumble into some thread-related issues with 1.0 beta 2 but they’ve been fixed in no time.
Processing JVM bytecode rather than Java source files means RoboVM can work with other JVM languages as well like Scala, Clojure, Kotlin, etc. which is great. And by the way the RoboVM compiler already supports Java 8 lambda expressions.
I previously started migrated an app from Objective C to Swift and despite using official Apple products I still had a fair share of annoyances with Swift/Objective C bridging, Xcode not yet supporting refactoring of Swift code etc. I have a feeling that using RoboVM instead may even be easier.
And finally I will be able to shared code between Android and iOS apps.