Environment
- MOE Version: 1.10.2
- Xcode Version: 16.4.0
- iOS SDK: 18.6 (simulator), also tested on device via TestFlight
- Target iOS: 14.0+
- Architecture: arm64
Problem Summary
My MOE-based iOS app crashes immediately on launch with two related errors:
Unknown class AppViewController in Interface Builder fileApplication windows are expected to have a root view controller at the end of application launch
The crash occurs before any Java code executes, suggesting that iOS is trying to instantiate storyboard view controllers before MOE has registered the Java classes with the Objective-C runtime.
Project Setup
Main.java (App Delegate)
package com.example.app.ios;
import org.moe.natj.general.Pointer;
import org.moe.natj.general.ann.RegisterOnStartup;
import org.moe.natj.objc.ObjCRuntime;
import org.moe.natj.objc.ann.ObjCClassName;
import org.moe.natj.objc.ann.Selector;
import apple.NSObject;
import apple.foundation.NSDictionary;
import apple.foundation.NSBundle;
import apple.foundation.NSOperationQueue;
import apple.uikit.UIApplication;
import apple.uikit.UIStoryboard;
import apple.uikit.UIViewController;
import apple.uikit.UIWindow;
import apple.uikit.c.UIKit;
import apple.uikit.protocol.UIApplicationDelegate;
@org.moe.natj.general.ann.Runtime(ObjCRuntime.class)
@ObjCClassName("Main")
@RegisterOnStartup
public class Main extends NSObject implements UIApplicationDelegate {
public static void main(String[] args) {
UIKit.UIApplicationMain(0, null, null, "Main");
}
@Selector("alloc")
public static native Main alloc();
protected Main(Pointer peer) {
super(peer);
}
private UIWindow window;
@Override
public boolean applicationDidFinishLaunchingWithOptions(UIApplication application, NSDictionary launchOptions) {
// Attempt to delay storyboard loading to allow MOE class registration
NSOperationQueue.mainQueue().addOperationWithBlock(new NSOperationQueue.Block_addOperationWithBlock() {
@Override
public void call_addOperationWithBlock() {
UIStoryboard storyboard = UIStoryboard.storyboardWithNameBundle("Main", NSBundle.mainBundle());
UIViewController initialViewController = storyboard.instantiateInitialViewController();
if (window != null) {
window.setRootViewController(initialViewController);
}
}
});
return true;
}
@Selector("setWindow:")
@Override
public void setWindow(UIWindow value) {
window = value;
}
@Selector("window")
@Override
public UIWindow window() {
return window;
}
}
AppViewController.java (Custom UITableViewController)
package com.example.app.ios.ui;
import org.moe.natj.general.Pointer;
import org.moe.natj.general.ann.RegisterOnStartup;
import org.moe.natj.objc.ann.ObjCClassName;
import apple.uikit.UITableViewController;
@ObjCClassName("AppViewController")
@RegisterOnStartup
public class AppViewController extends UITableViewController {
@Selector("alloc")
public static native AppViewController alloc();
protected AppViewController(Pointer peer) {
super(peer);
}
// ... view controller implementation
}
Main.storyboard
The storyboard references AppViewController as the custom class for the initial table view controller:
<tableViewController id="..." customClass="AppViewController" sceneMemberID="viewController">
Info.plist
Currently configured with LaunchScreen as the main storyboard (as a workaround attempt):
<key>UIMainStoryboardFile</key>
<string>LaunchScreen</string>
What I’ve Tried
-
Removed
UIMainStoryboardFilefrom Info.plist and created the window programmatically inapplicationDidFinishLaunchingWithOptions- still crashes -
Set
UIMainStoryboardFiletoLaunchScreen(a native storyboard with no custom classes) and delayed loadingMain.storyboardusingNSOperationQueue.mainQueue().addOperationWithBlock()- still crashes -
Added
@Runtime(ObjCRuntime.class)and@ObjCClassName("Main")annotations to the app delegate class - still crashes -
Removed
UIApplicationSceneManifestfrom Info.plist to opt out of scene-based lifecycle - still crashes -
Verified OAT/ART files are correctly embedded in the app bundle - confirmed they are present and correctly linked
Crash Report Analysis
The crash happens in UIApplicationMain → scene lifecycle → storyboard instantiation, before any MOE Java code has a chance to execute. The key errors are:
Unknown class AppViewController in Interface Builder file.
Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Application windows are expected to have a root view controller at the end of application launch'
MOE Framework Build Info
From the embedded MOE.framework:
- Built with Xcode 16.2, iOS SDK 18.2
- Running on Xcode 16.4.0, iOS 18.6 simulator
Questions
-
Is there a known incompatibility between MOE 1.10.2 and iOS 14+ scene lifecycle management?
-
Is there a way to force MOE to register Java classes with the Objective-C runtime before
UIApplicationMainis called? -
Has anyone successfully run a MOE app with storyboards on iOS 18 / Xcode 16.4?
-
Are there any workarounds to ensure Java-backed view controllers are available when iOS tries to instantiate them from storyboards?
Any guidance would be greatly appreciated. This project was working with Xcode 12 and older iOS versions, but the migration to Xcode 16.4 has exposed this timing issue.