Cross-Platform Basics

The app we will be creating is a version of the unix fortune program. Each time the user presses a button, a random fortune will be displayed. The UI will be intentionally simple in order to focus on strategies for cross-platform development.

Project Setup

Open up RoboVM Studio (or IntelliJ) and choose Start New Project....

Under the RoboVM section choose the Cross Platform template. Fill in the details for your project in the next two steps as normal, and finally click the Finish button.

Core

The core module should contain code that does not depend on the Android or iOS platforms. Typically, this will be comprised of the data and business logic layers.

Our model for the fortune app will start out with the simplest API possible, retrieving a random quote from a static database. In later sections we will expand that functionality to cover pulling new quotes from a web service.

In the process, you will learn about using popular libraries from the Android ecosystem in a cross-platform way.

The first thing we need to do is rename CounterStore to FortuneStore and replace the code with the following:

Android UI

In order to design the Android UI, we will be taking advantage of the Android Designer within RoboVM Studio. In the project view, navigate to the android module > src > main > layout, and double click on activity_fortune.xml.

The cross platform template has already set us up with a TextView and a Button, so all we need to do is move the button to the bottom of the screen, and change its text to "Show Next Fortune!".

Also, we probably want to modify the id property of the label and button to fortuneTextView and nextFortuneButton respectively.

Replace the contents of FortuneActivity.java under the android module with the following code.

[:2:] Obtain references by id to the text view and button we created in the Android Designer.

[:3:] Hook up the click event of the button to show a new quote from the model.

iOS UI

In order to design the UI for the iOS app, we will make use of Interface Builder from within Xcode. In the project view, navigate to the ios module > resources > Base.lproj, and double click on Main.storyboard.

There should already be a label and a button, which is exactly what we need, after some minor configuration.

Click on the button and clear its constraints.

Change the text to be Show Next Fortune.

Resize it so all the text is shown.

Move it until the guides show it centered horizontally and next to the bottom.

Update the button's constraints.

Click on the label and clear its constraints.

Make sure the label's text size is 24.

Change the alignment to 'left aligned'.

Increase the Lines spinner to around 10.

Drag the bottom edge of the label down almost all the way to the bottom to resize it.

Update the label's constraints.

Finally, back in IntelliJ, update the contents of MyViewController.java in the ios module to the following code.

[:2:] Obtain a reference to the label we created in Interface Builder.

[:3:] Hook up the IBAction of the button to set a new fortune on click.

Web Request

At this point, we have a simple static database of fortunes, but in a typical application we would probably pull data from a web service. A commonly used library for making RESTful queries and parsing data into a Java object is Retrofit.

Configuration

First we will add Retrofit as a dependency to the core module by editing its build.gradle file:

Fortune Client

We are going to use the quotes subreddit as a web service that provides a simple API to query a random quote. Each subreddit can be queried just by adding '.json' to the end of the url. We will be using Retrofit to perform an asynchronous http GET, and parse the json result into a Java object.

We are going to create the simplest possible Retrofit client to access this service. Add a new class called FortuneClient to the core module and paste in the following code.

[:1:] The json that returned is just a simple data object with an array of nested children data objects.

[:2:] Retrofit needs an interface with methods for each endpoint. There are many options available, but ours is very simple. If you provide a method that has a Retrofit.Callback argument, the http request will be asynchronous.

[:3:] For now, we provide a simple listener that the android and ios modules will be able to use to get the quotes when the request finishes.

[:4:] In the constructor we create a service using the FortuneService interface defined earlier, which can be reused by each request.

[:5:] For the sake of simplicity, we return the quote if the request is successful, and the error message if not.

Android Updates

First we need to replace the FortuneStore in our activity with our new FortuneClient:

The button click will be handled pretty much like on Android as well, accept Retrofit does not yet have direct support for RoboVM. On Android, the callback defaults to being called on the main UI thread, but on iOS (for now at least) the callback will most likely be called on the same thread used to make the http request.

In order to update the UI on the main thread, we will use an iOS feature called the DispatchQueue. Change the 'clicked' method and add the following 'setFortune' method to our view controller.

INFO: When you run the app in the simulator now, you may see warnings when the button is clicked that some classes are missing. Retrofit will use certain libraries if they are present, and it looks these up at runtime using reflection. These can be ignored for now, but you may need to add these to the forceLinkClasses section of the robovm.xml in the future.

Future Work

At this point we have a working, if somewhat simple, networked fortune app. There are many topics we haven't discussed, like storing our favorite quotes to the FortuneStore, serializing the store to disc in a cross-platform way.

We will be covering those topics and more in future tutorials!

Conclusion

With RoboVM you can use Native UX and platform libraries, at native speeds, while also staying within the comfort of the tools and JVM ecosystem that you already know. Large portions of code can now be shared between your iOS and Android app, and some libraries from the Android community already work with very little effort on your part.