My experience with jME on iOS

@erlend_sh kindly asked me to write about my iOS experience, so I’m pleased to accommodate that wish.

Currently I have two apps on the app store and I’m using jmonkeyplatform-3.0.10 and Xcode 7.3.1. NEW: The minimum iOS version is 6.0. (You could support earlier versions, but this requires to use an old Xcode version. Not recommended!)

Please read the official jME iOS documentation, I just describe the pitfalls.

After enabling iOS in your netbeans project on your Mac and after building the project with “ant jar”, you can open Xcode and run your project.

  1. Sometimes (very often) only half of the screen is used by the engine. (Device and simulator.) You can rotate the device to fix, or call JmeAppHarness.appReshape(width, height) at startup if there is a difference between nifty and app context resolution.
  2. If a class is missing you should add it to the ProGuard exclusion list in before you invest time in further investigation.
    1. UTF-8 encoding works fine. To support all other encodings, it is required to add this to the ProGuard exclusion list:
      -keep public class * extends java.nio.charset.Charset { *; }
    2. Annotations require these ProGuard exclusions:
      -keep public class java.lang.reflect.Proxy { *; } \
      -keep public class java.lang.reflect.InvocationHandler { *; }
    3. UPDATE: Download http(s) via requires a huge exclusion list and for https furthermore to patch the OpenJDK source code. Obviously Sun did not expect to run it on other platforms. :scream: Alternative: Use native code. That would also support a proxy.
  3. Testing with the simulator is not sufficient. It only tests x86 code, but you want to test the ARM code as well. This means test on a 32 bit and 64 bit device. Also test debug, ad-hoc and app store installation. Xcode provides many possible misconfigurations. If you omit one test, you can't be sure that it will run everywhere. Examples: ZipInputStream() and require ISO-8859-1 Test with Charset.availableCharsets(). (Crashes without ProGuard exclusion.)
  4. nifty-gui needs also additional care:
    1. Not all nifty components will work: for example textfield and label are unusable. (Use Picture class or native input dialogs.)
    2. Image buttons seem not to react or react if clicked somewhere else. The reason is that nifty internally uses another resolution. My workaround is to store a list of the buttons and determine a click manually, which gives you the chance to correct the coordinates. I did this for portrait mode only, landscape looked more complicated. (If your app requires to support multi-touch you need that list anyway.)
    3. NEW: To calculate the scale factor for the previous item, I just look at the resolution and get the scale factor from this table: ultimate-guide-to-iphone-resolutions

      I noticed that iPhone 6 and iPhone 6 Plus allow to enable a display zoom.
      Unfortunately the iPhone 6 Plus resolutions are ambiguous, so that you have to call a native function to find out if it is a zoomed iPhone 6 Plus. (And also the iPhone 6 Plus simulator has a different resolution.)

      So does that mean you have to get all iPhone/iPad models to test with? Yeah…

  5. If your images are not power-of-two, you need to convert them.
  6. com.jme3.font.BitmapText shows nothing. Use Picture class instead.
  7. Images have to be flipped. Picture constructor has a flag to be set, nifty-gui requires flipped material (the flag in TextureKey is ignored). NEW: Fixed in 3.1
  8. Images need to be 32 bpp. Don't use other color depths, fix jme-ios.m (IDK how) or use other image format (j3o). Otherwise you see this error message:
    <Error>: CGBitmapContextCreate: invalid data bytes/row: should be at least 1056 for 8 integer bits/component, 3 components, kCGImageAlphaPremultipliedLast.
    <Error>: CGContextDrawImage: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context  and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update.
  9. Checklist:
    1. PROBLEM: Your app runs in the Xcode simulator, but fails on your iOS device: SOLUTION: Update iOS plug-in! (See next item.)
    2. PROBLEM: Your app runs on iOS device with 32 bit CPU, but fails on 64 bit CPU: SOLUTION: Update Avian inside the iOS plug-in. (No so straight forward. Buy enough coffee.) You have to get the latest source from github.
    3. PROBLEM: Debug installation works fine, also app store validation, but not ad-hoc installation: SOLUTION: Change Xcode project settings. This is the most nasty part of all. Try your luck at stackoverflow (google for exact error message).
    4. PROBLEM: Latest Xcode SOLUTION: You need to update ios.version to 9.3 in and (UPDATE:) ENABLE_BITCODE=NO in build settings. To run on a 64-bit CPU you need to add ARM64 to VALID ARCHITECTURES. To run in the simulator you have to remove that again.
  10. If the SDK iOS plug-in is updated, you have to do these steps:
    1. Make backup!
    2. Update SDK plug-in.
    3. Disable iOS support in the project.
    4. Delete project/ios
    5. Delete project/resources/ios
    6. Delete ~/Library/Application Support/jmonkeyplatform/3.0/avian-openjdk
    7. Enable iOS support again.
    8. Copy/merge your backup to project/ios.

Wow, I should have written a book. :wink:

It looks a bit scaring… but the problems don’t come all at once. And I solved most problems by searching the forum or asking funny questions.

To be continued…


Hey, thanks for assembling this info, I’m sure this will help a lot of people including us to improve the jME iOS support!

This is a huge step for JME, thanks a lot for your feedback

Fantastic writeup, this is a great help. I hope you’ll keep doing projects with jMonkeyEngine. I’m sure it’d be mutually beneficial!


You’re welcome. And good to know, because I have some Android questions… :blush:

Thank you for posting this.

Thanks for posting this. Ive got a game that I’ve had working on Android, Windows, Linux for 2 months now but I’m having major issues with porting to IOS since I included a GUI.

I’ve tried Nifty, Lemur and TonegodGUI and my preferred option is TonegodGUI as works very well and doesnt give the unpredictable results that you seem to get from Nifty.

However, I can’t get TonegodGUI or Lemur to run on IOS no matter what I try.

Have you had any experience of running TonegodGUI on IOS?

Thanks for the tip about PROGUARD exceptions. It’s obvious but I completely overlooked that until your post and I did get a lot further except it seems to to be still trying to use reflection even though I disabled JIT complilation as suggested (and as is now in the official release).

I’m so close to abandoning all GUIs and writing my own but if I can avoid that it would be nice.

Do you have any suggestions?

Have you tried ROBOVM as an alternative to AVIAN? Does that work with JME?

Sorry forgot to mention. I have tried Nifty but I can’t get text to render properly even with buttons. Native controls are absolutely not an option because the whole point is to be 100% cross platform.

It might not be an issue with any of the GUI lib, It sounds like it’s more a general issue, like BitmapText failing.

Hi, It only started to happen once I added the GUI. In the first phase of the app I was simply loading models into the scene graphs and just using the includes from the BASICGAME template.

As soon as I added the GUI. Tonegod and Lemur wont run on IOS at all. Nifty will run but the output is unpredictable.

I tried creating a brand new project with just the GUI alone to make sure it was not something else and the results were the same.

Even the standard demos from all 3 GUIs wont work on IOS.

I’m guessing it may be something to do with AVIAN or the IOS build script because it runs on all other platforms without a problem.

Yes my point is, writing your own GUI may not solve the issue.

Ah I see what you mean. My intention was to use Spatials as the GUI components and create custom controls on them for any specific behaviour I need.

The advantage being I’ve already proven they work fine on IOS and have the advantage of being part of the scene graph. Obviously that comes with it’s own challenges but the point is, I’d be using tried and tested components.

The obvious issue is the fact I may need to compromise on things like effects etc. for expedience and also it’s not likely to be as transferrable to other apps in the initial stages.

I hate re-inventing the wheel, but sometimes you have to accept a square wheel is never going to work and start again. :smile:

Can you tell me where the dependence on sun.java2d.DisposerRecord came from?

I wrote these notes a long time ago. I think that “ant jar” failed with an error message that DisposerRecord is missing, but I can’t remember why it was required. I’ll check that tomorrow.

It will be included in the iOS plugin release that I am committing right now, if its still reported as missing you’d have to add it to the proguard directives. Before we excluded it from the reduced OpenJDK classpath thats distributed with the iOS plugin.

1 Like

No. I found no postings that someone ever tried. And I was already familiar with nifty.

The best solution for me is to use nifty for arranging image buttons and to keep track myself if touched.
Using a Picture object may be an alternative, but requires that there is no background scene. IMHO not as good as nifty.
That nifty and all other GUIs have problems may come from the fact that iOS requires power-of-two images. To have a chance to get it running you have to dig into it and replace all images nifty wants to load. (For example the cursor for textfields.) Nifty does not scale its text, so even if you get it running, it may become unreadable on bigger screens.

No. There are some postings here why it was not considered as alternative.

I’m just testing the new iOS plug-in. Looks like this is going to be a longer debug session…

In the meantime here are some fixes:


vmArgs.nOptions = 5; // array out of bounds (seems that it doesn't harm)


int wdth = CGImageGetWidth(inImage);
int ht = CGImageGetHeight(inImage);


// fixed order of width and height (now it's obvious why square images always scale correctly)
CGContextRef context = CGBitmapContextCreate(rawData,wdth,ht,bitsPerComponentImg,bytesPerRowImg,colorSpace,kCGImageAlphaPremultipliedLast| kCGBitmapByteOrder32Big);

you are a superstar. Can’t wait to hear anything else you find.

Great work.

By the way, vmArgs.nOptions = 5 is not out of bounds. It’s a zero based array and the 5th option is related to logging.

C’mon. :blush:

The array length was too small.

1 Like