Guest posts Plugins

RoboVM and the move to the IntelliJ Platform

The following is a guest blog post by Mario Zecher from RoboVM

In the beginning…

Sometime in early 2010, Niklas Therning formed the crazy idea of bringing Java and other JVM languages to iOS. Being a good hacker citizen, Niklas naturally started implementing his crazy idea: RoboVM was born.

Since then, RoboVM has grown in functionality, stability and most of all scope: we want to make cross-platform mobile development as enjoyable as possible for everyone in the Java/JVM ecosystem. Back in March 2015, after much frantic work, blood and tears, we reached the first milestone of this goal: RoboVM 1.0 was released.

What’s in it?

At its core, RoboVM is an ahead-of-time compiler for JVM byte code, that uses LLVM to create high-performance native machine code. On top of this, RoboVM provides both a Java runtime class library based on Android as well as a custom native bridge to access iOS APIs via idiomatic Java. Here’s what that looks like:

public class MyViewController extends UIViewController {
  private final UIButton button;
  private final UILabel label;
  private int clickCount;

  public MyViewController() {
     // Get the view of this view controller.
     UIView view = getView();

     // Setup background.
     view.setBackgroundColor(UIColor.white());

     // Setup label.
     label = new UILabel(new CGRect(20, 250, 280, 44));
     label.setFont(UIFont.getSystemFont(24));
     label.setTextAlignment(NSTextAlignment.Center);
     view.addSubview(label);

     // Setup button.
     button = UIButton.create(UIButtonType.RoundedRect);
     button.setFrame(new CGRect(110, 150, 100, 40));
     button.setTitle("Click me!", UIControlState.Normal);
     button.getTitleLabel().setFont(UIFont.getBoldSystemFont(22));

     button.addOnTouchUpInsideListener((control, event) => {
     label.setText("Click Nr. " + (++clickCount));
     });

     view.addSubview(button);
  }
}

Not only can you call into native iOS APIs from within Java, you can have your Java classes implement Objective-C protocols or subclass Objective-C classes, all within the cosy confines of the JVM language of your choice. RoboVM also has full support for JDWP debugging on both the iOS simulator and iOS devices, and integrates with Xcode’s Interface Builder, a WYSIWYG editor to create iOS UIs.

Screen Shot 2015-08-25 at 15.02.27

We love the Java/JVM ecosystem, so naturally we wanted you to be able to exploit it to its full extent. On top of the iOS specific functionality, we also added support for Maven, Gradle, JUnit, and created an Eclipse plugin.

The community around the project contributed the first iterations of NetBeans and IntelliJ IDEA support. In the alternative JVM language department, the community provided support for Clojure and Scala. Kotlin just worked(tm) out of the box via Gradle.

Finally, we managed to get JavaFX working on IOS. We got JavaFX working on iOS in early 2013. Since late 2014 we’ve been actively working with LodgON to improve JavaFX on iOS.

This was the status of our 1.0 release in March. We felt pretty good about it, but there was one thing that kept nagging us. Our goal was to enable cross-platform mobile development for both iOS and Android. But our tooling was mostly focused on iOS.

Decisions

For cross-platform development, users have to setup a JDK, Xcode, the Android SDK and then try to wire up multiple projects for iOS, Android and shared code, praying that everything works as intended.

We wanted a more unified experience for our users in form of an IDE. It should cover both Android and iOS development, and also provide a great experience for alternative JVM languages. We needed a foundation for this IDE.

Our options came down to Eclipse, IntelliJ IDEA Community Edition and Android Studio.

At the time 100% of our team was using Eclipse (50/50 split now :)), and we already had a very mature Eclipse plugin. It felt like the natural first choice. However, there were a few reasons we eliminated Eclipse:

  • No Android support and the Eclipse Android development tools had been deprecated

  • No mature Gradle support, a requirement for Android development

  • Less mature tooling for alternative JVM languages, such as Scala or Kotlin

With Eclipse out of the race, the fight was between Android Studio and IntelliJ IDEA Community Edition. Here’s why IntelliJ IDEA won:

  • Android Studio is lagging behind IntelliJ IDEA in features. We wanted to be on the cutting edge especially with regards to alternative JVM languages like Kotlin

  • Android Studio is entirely focused on Android development making it very hard to do anything other than that. IntelliJ IDEA is inclusive of the entire Java/JVM ecosystem.

  • The IntelliJ IDEA Android plugin has almost all functionality, except for the latest addition of JNI

Baby Steps – the RoboVM IntelliJ IDEA Plugin

Screen Shot 2015-08-25 at 17.19.19

To get our feet wet with IntelliJ IDEA, we started to rewrite the RoboVM plugin for IntelliJ IDEA, contributed by Ashley Williams.

So, how do you write an IntelliJ IDEA plugin? At the time, the new IntelliJ Platform SDK Documentation wasn’t in place yet, and the old documentation was quite lacking.

Luckily for us, many people before us went down this road, and most of that work was open-source. With a mixture of incomplete docs, sources of other plugins and the extremely helpful JetBrains folks on the OpenAPI and plugin development forum, we were able to completely rewrite the plugin, with some nice features:

I’m sure we did a lot of things wrong, but at the end of the day, most things where quite straight forward. The UI editor made creating all required dialogs and other UI components a breeze, and the edit, run, debug cycle was also quite fast, especially with class reloading.

What was less nice, was the complete lack of build and dependency management system support for IntelliJ IDEA plugins. Usually, IntelliJ IDEA plugins are written and build as IntelliJ IDEA projects. That didn’t work so well with the rest of our build infrastructure, making nightly builds and integration with RoboVM dependencies impossible.

After some trial and error, we found a way to use Maven for build and dependency management of our IntelliJ IDEA plugin. It’s not pretty but it does its job well. To be able to import this Maven project and have it recognized as an IntelliJ IDEA plugin project, we also needed to use this very old plugin.

The last big issue we faced was our reliance on Java 7 features. On Mac OS X, IntelliJ IDEA required Apple’s Java 6 or a custom build of OpenJDK that JetBrains bundles optionally. Our RoboVM code would crash and burn on Apple’s Java 6. For our users, this meant they had to manually modify IntelliJ IDEA’s Info.plist file to ensure Java 7 was picked as the runtime. This has luckily been fixed in the latest IntelliJ IDEA releases.

One IDE to rule them all – RoboVM Studio

With our plugin being completely rewritten and test-driven by our community, we started looking into forking IntelliJ IDEA Community Edition.

It took a while to understand both the structure and build system of IntelliJ IDEA Community Edition. We wanted to keep our changes to a minimum, essentially adding RoboVM Studio branding, integrating the Android and RoboVM IntelliJ IDEA plugins, and bundling it all up into a nice DMG. This should allow us to stay as close to upstream as possible, and being able to integrate the latest and greatest features on the day they are released.

Sadly, the build system was even less documented than plugin development. We opted to create a copy of the standard build system and modify that copy to our needs. The build system is largely based on Gant, an Ant variant that uses Groovy for scripting. It took us quite some time to figure out the bits and pieces, but we eventually ended up with a workable solution that would integrate our branding, the RoboVM IntelliJ IDEA plugin and the Android IntelliJ IDEA plugin in one nice package.

The next problem we faced was the versioning scheme and the update/patching mechanism. The IntelliJ platform uses one main entry point in form of an XML file that specifies a products parameters, from versions to update/patch urls, splash screens and so on. It turned out that our versioning scheme (x.x.x) was incompatible with the scheme used by IntelliJ-based products. We simply patched the code responsible for parsing versions and moved on.

Finally, we had to come up with a way to properly perform snapshot and release builds of RoboVM Studio. Since Gant is not a dependency management system, we had to resort to some brute force methods. We build the RoboVM IntelliJ IDEA and Android IntelliJ IDEA plugins in tree, using Git tags for versioning. Then we pull in the JAR files as plugins to be bundled with the build artifact.

Despite all this sounding like a bad experience, it only took us about a week to get the first version of RoboVM Studio into the hands of our users. JetBrains also reworked their build system a little since then, making it easier to create products based on IntelliJ IDEA Community Edition. We are in the process of transitioning to this new build, which should make our lives simpler and our setup saner.

Was it worth it?

Definitely. IntelliJ gives us a great foundation for new features down the road. The feedback from users has been extremely positive since the release of RoboVM Studio. Our goal with RoboVM Studio was to provide a unified, streamlined and easy to setup experience for our first time users. We believe we have come very close to this goal already.

We’d like to thank JetBrains for providing the community with the IntelliJ platform, as well as for their informal support during the development of both the RoboVM IntelliJ IDEA plugin as well as RoboVM Studio.

If you’ve built on the IntelliJ Platform, let us know. We’d love to hear your story.

image description

Discover more