How to make an iOS app using JavaFX 8

Summary

Updated on Juli, 9, 2013

JavaFX8 on iOSJavaFX using CSS on iOS

After showing two pics of “JavaFX 8 on iPhone” on twitter and openjfx mailing list, many many people asked me how did I do that. So here is the magic:

My JavaFX app for iOS using three main technologies:

  1. OpenJFX 8
  2. RoboVM 0.0.2
  3. Android SDK (currently no OpenJDK!)

To make an iOS app using JavaFX8 and RoboVM you have to do the following steps:

  1. Download, patch and build OpenJFX8.
  2. Download and install RoboVM (0.0.2)
  3. Download my sample project “JavaFX4iOS“, (open it in IntelliJ) and call the “compile” and “runOnDevice” ant task.

Thats it!

  

Details

To build a JavaFX app for iOS you have to go through 3 main steps:

  1. Download, patch and build OpenJFX 8
  2. Download RoboVM.
  3. Build JavaFX app for iOS using RoboVM.


Step 1: Downloading, patching and building OpenJFX 8

To use JavaFX 8 we need to build it by ourself because there is no official release version of JavaFX for iOS. But it’s no problem because the guys from Oracle have build an excellent gradle based build system for JavaFX which works for Windows, Mac and iOS (Linux and Android coming soon).

To download and build OpenJFX 8 follow the instructions here: https://wiki.openjdk.java.net/display/OpenJFX/Building+OpenJFX

  1. Create a new directory “openjfx” (e.g. /Applications/Java/openjfx), open a terminal and go to this directory by “cd /Applications/Java/openjfx)
  2. Now download the JavaFX source code: “hg clone http://hg.openjdk.java.net/openjfx/8/graphics/rt"
  3. Build OpenJFX for iOS: “gradle -PCOMPILE_TARGETS=ios sdk”
  4. The final iOS build is located here: /Applications/Java/openjfx/rt/build/ios-sdk

To use currenty OpenJFX on iOS you’ll need to patch one class which has a reference to the OpenJDK which is not used by RoboVM:

  • <openjfx>rt/modules/graphics/src/main/java/com/sun/javafx/css/StyleCacheEntry: changepublic int hashCode() {int hash = Double.hashCode(fontSize);…} to public int hashCode() {int hash = (int) (super.hashCode()+fontSize);…}
    It’s needed because the android class library used by RoboVM does not contain the new Java 1.8 based Double.hashCode(int) method…


Now we are ready to rebuild OpenJFX for iOS:

  • Open terminal
  • cd /Applications/Java/openjfx/rt
  • gradle -PCOMPILE_TARGETS=ios sdk
  • you’ll find the iOS sdk under /Applications/Java/openjfx/rt/build/ios-sdk


Step 2: Download and install RoboVM

To deploy an Java based iOS app on the iPhone we need one single binary which does not contain any dynamic library (“dylib”). At the moment  we can not use the standard JavaSE VM because this VM uses JIT technology which is not allowed on iOS devices. What we need is a VM which uses a AOT compiler. AOT means “ahead-of-time” – so the Java source code will not be translated to Java byte code, it will be translated to real native machine code. So after using an AOT compiler you don’t need a Java VM anymore to start your app. Thats great! And that is what RoboVM (or Avian) can do for us: “The RoboVM compiler translates Java bytecode into native ARM or x86 code. Apps run directly on the CPU. No interpreter or virtual machine involved.” (http://www.robovm.org)

Ok so now we have to download the latest version of RoboVM (0.0.2) from here: http://www.robovm.org Please than follow the installation instructions here: http://www.robovm.org/docs.html#getting-started BTW: There will be a maven plugin for using RoboVM available very soon: http://www.zenjava.com/2013/06/09/robovm-maven-plugin-beta-release/

Step 3: Building JavaFX app for iOS using RoboVM 

  1. To use JavaFX8 with RoboVM we need to insert several classes from OpenJDK which are missing in the Android class library. These are: sun.util.logging.LoggingProxysun.util.logging.LoggingSupportsun.util.logging.PlatformLogger. These classes have to be inserted in your project and classpath. Otherwise you’ll get an “ClassNotFoundException” when starting your app in RoboVM.
  2. To use the native font rendering on iOS we have to set two properties in the main method:System.setProperty(“glass.platform”, “ios”);
    System.setProperty(“prism.text”, “native”);
  3. To use JavaFX with RoboVM on iOS we need a special “Start” class which loads the JavaFX app into the cocoa touch UI.
    /*
     * User: Tobias Bley
     * Date: 04.07.13 14:16
     * Copyright (c) 2013. UltraMixer Digital Audio Solutions Tobias Bley & Matthias Haenel GbR, Germany
     */
    
    package com.ultramixer.javafx4ios;
    
    import javafx.application.Application;
    import org.robovm.cocoatouch.foundation.NSAutoreleasePool;
    import org.robovm.cocoatouch.foundation.NSDictionary;
    import org.robovm.cocoatouch.uikit.UIApplication;
    import org.robovm.cocoatouch.uikit.UIApplicationDelegate;
    
    public class Start extends UIApplicationDelegate.Adapter
    {
        @Override
        public boolean didFinishLaunching(UIApplication application,
                                          NSDictionary launchOptions)
        {
    
            Thread launchThread = new Thread()
            {
                @Override
                public void run()
                {
                    Application.launch(Main.class);
                }
            };
            launchThread.setDaemon(true);
            launchThread.start();
    
            return true;
        }
    
        public static void main(String[] args) throws Exception
        {
            System.setProperty("glass.platform", "ios");
            System.setProperty("prism.text", "native");
    
            NSAutoreleasePool pool = new NSAutoreleasePool();
            UIApplication.main(args, null, Start.class);
            pool.drain();
        }
    }
  4. Now you have to compile your source code.
  5. To let RoboVM make an iOS app we need to call: /opt/robovm/bin/robovm -verbose -properties robovm.properties -config robovm.xml -arch x86 -run -ios-sim-family iphone
  6. As you can see we need two more files: robovm.properties and robovm.xml

robovm.xml defines all the Java libs (classpath) and native libs (*.a files from JavaFX). robovm.properties defines name, version, appID, … for the iOS app.

<config>
    <executableName>${app.executable}</executableName>
    <mainClass>${app.mainclass}</mainClass>
    <os>ios</os>
    <arch>thumbv7</arch>
    <target>ios</target>
    <iosInfoPList>Info.plist.xml</iosInfoPList>
    <roots>
        <root>sun.util.logging.PlatformLogger</root>
        <root>com.sun.javafx.tk.quantum.QuantumToolkit</root>
        <root>com.sun.prism.es2.ES2Pipeline</root>
        <root>com.sun.prism.GraphicsPipeline</root>
        <root>com.sun.prism.es2.IOSGLFactory</root>
        <root>com.sun.glass.ui.ios.**.*</root>
        <root>com.sun.prism.shader.**.*</root>
        <root>com.sun.scenario.effect.impl.es2.ES2ShaderSource</root>
        <root>com.sun.javafx.font.coretext.CTFactory</root>
        <root>com.sun.scenario.effect.**.*</root>

    </roots>
    <classpath>
        <!--
         NOTE: These entries will be ignored when building from within Eclipse or using the Maven plugin. The
         Eclipse/Maven classpath will be used instead.
        -->
        <!-- Base libs needed for every project //-->
        <classpathentry>/opt/robovm/lib/robovm-objc.jar</classpathentry>
        <classpathentry>/opt/robovm/lib/robovm-cocoatouch.jar</classpathentry>
        <classpathentry>javafx/ext/jfxrt.jar</classpathentry>

        <!-- Libs for my project //-->
        <classpathentry>bin</classpathentry>
        <classpathentry>libs/miglayout-core-4.2.jar</classpathentry>
        <classpathentry>libs/miglayout-javafx-4.2.jar</classpathentry>
    </classpath>
    <libs>
        <!-- ARM libs //-->
        <lib>javafx/libprism-es2-armv7.a</lib>
        <lib>javafx/libglass-armv7.a</lib>
        <lib>javafx/libdecora-sse-armv7.a</lib>
        <lib>javafx/libjavafx-font-armv7.a</lib>
        <lib>javafx/libjavafx-iio-armv7.a</lib>
        <lib>javafx/libprism-common-armv7.a</lib>

        <lib>native/arm/libTestIOSLibrary.a</lib>


        <!-- i386 libs //-->
        <lib>javafx/libprism-es2-i386.a</lib>
        <lib>javafx/libglass-i386.a</lib>
        <lib>javafx/libjavafx-font-i386.a</lib>
        <lib>javafx/libjavafx-iio-i386.a</lib>
        <lib>javafx/libprism-common-i386.a</lib>

        <lib>native/i386/libTestIOSLibrary.a</lib>

    </libs>
    <frameworks>
        <framework>UIKit</framework>
        <framework>OpenGLES</framework>
        <framework>QuartzCore</framework>
        <framework>CoreGraphics</framework>
        <framework>ImageIO</framework>
        <framework>MobileCoreServices</framework>
        <framework>CoreText</framework>
    </frameworks>

</config>

#
#Sat May 18 11:09:28 CEST 2013
app.version=1.0
app.id=com.ultramixer.JavaFX4iOS
app.mainclass=com.ultramixer.javafx4ios.Start
app.executable=JavaFX4iOS
app.build=1
app.name=JavaFX4iOS

To simplify the build process I made a little ant file (build.xml). So to compile your app simply call “compile” ant target and to start it on the iPhone simulator call “runOniPhoneSimulator” or “runOnDevice” for a real iPhone / iPad.

<project name="JavaFX4iOS">

    <target name="compile">
        <mkdir dir="bin"></mkdir>
        <delete dir="bin" includes="**/*" includeemptydirs="true"></delete>
        <copy todir="bin">
            <fileset dir="src" includes="**/*.png"></fileset>
            <fileset dir="src" includes="**/*.css"></fileset>
            <fileset dir="src" includes="**/*.fxml"></fileset>
        </copy>
        <javac bootclasspath="/opt/robovm/lib/robovm-rt.jar" destdir="bin" srcdir="src">
            <classpath
                    path="/opt/robovm/lib/robovm-objc.jar:/opt/robovm/lib/robovm-cocoatouch.jar:javafx/ext/jfxrt.jar">
                <fileset dir="libs">
                    <include name="**/*"></include>
                </fileset>
            </classpath>

        </javac>
    </target>

    <target name="runOnDevice">
        <exec command="/opt/robovm/bin/robovm -verbose -properties robovm.properties -config robovm.xml -run">
        </exec>
    </target>

    <target name="runOniPhoneSimulator">
        <exec command="/opt/robovm/bin/robovm -verbose -properties robovm.properties -config robovm.xml -arch x86 -run -ios-sim-family iphone">
        </exec>
    </target>

    <target name="runOniPadSimulator">
        <exec command="/opt/robovm/bin/robovm -verbose -properties robovm.properties -config robovm.xml -arch x86 -run -ios-sim-family ipad">
        </exec>
    </target>
</project>

To submit your app to the AppStore we need the Info.plist.xml file too which defines the behavior of the app (e.g. orientation).

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>CFBundleDisplayName</key>
    <string>${app.name}</string>
    <key>CFBundleExecutable</key>
    <string>${app.executable}</string>
    <key>CFBundleIdentifier</key>
    <string>${app.id}</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>${app.name}</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>${app.version}</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>${app.build}</string>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>UIDeviceFamily</key>
    <array>
		<integer>1</integer>
		<integer>2</integer>
	</array>
    <key>UIRequiredDeviceCapabilities</key>
    <array>
        <string>armv7</string>
    </array>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
        <string>UIInterfaceOrientationPortrait</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
</dict>
</plist>

Summary

To make an iOS app using JavaFX8 and RoboVM you have to do the following steps:

  1. Download, patch and build OpenJFX8.
  2. Download and install RoboVM (0.0.2)
  3. Download my sample project, (open it in IntelliJ) and call the “compile” and “runOnDevice” ant task.

Thats it!

 

Downloads

Feel free to download my JavaFX4iOS sample project on Bitbucket: https://bitbucket.org/software4java/javafx4ios

Last words

Many many thanks to the people from Oracle and their committers who did a really really good job! Besides main performance issues we are very close to “Using JavaFX to submit iOS apps to the AppStore”. I investigated many hours to discover all the issues about OpenJFX, RoboVM and android class library. Please participate on JavaFX jira system to discuss and commit bug fixes. If you want to support our further work on “JavaFX on iOS” feel free to donate some bucks. The main goal for all the JavaFX is now to optimize the whole build process and the main performance so that we can really submit our first JavaFX based iOS app to the AppStore!

Donate some bucks:


Donate 10$:

Follow me on Twitter:



Best regards,

Tobi
software4java
UltraMixer Digital Audio Solutions
www.ultramixer.com

3 Comments

  • Pingback: Java desktop links of the week, July 8 « Jonathan Giles

  • July 12, 2013 - 4:34 am | Permalink

    Hi,
    my question is that i don’t have mac os, so can i develop using windows or linux.
    Caz if i wont have mac i can never ever tests the code… :’(
    ne suggestion are appreciated…

  • Pingback: Java-App für iOS mit JavaFX 8 und RoboVM, ausführliche Anleitung - Java Blog | Javainsel-Blog

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>