eby
eby

Reputation: 191

Build error with MFMailComposeViewController in a libGDX / RoboVM project

I'm trying to add functionality to an existing iOS libGDX app (using RoboVM) so that when a scene2d.ui button is pressed, an MFMailComposeViewController popup is displayed with a predefined set of CSV files attached that are stored in the app's Library > local folder. Here's the code I have so far:

Imports & members:

import org.robovm.apple.foundation.*;
import org.robovm.apple.messageui.MFMailComposeResult;
import org.robovm.apple.messageui.MFMailComposeViewController;
import org.robovm.apple.messageui.MFMailComposeViewControllerDelegate;
import org.robovm.apple.uikit.*;
import org.robovm.objc.ObjCObject;

private UIWindow mailWindow;
private UIViewController mailViewController;
private MFMailComposeViewController mailPicker;

Methods:

private void createMailWindow() 
{
    mailWindow = new UIWindow(UIScreen.getMainScreen().getBounds());
    mailViewController = new UIViewController();
    mailWindow.setRootViewController(mailViewController);
}

private void sendEmail(String fileName, String subject, String body) 
{
    mailPicker = new MFMailComposeViewController();
    mailPicker.addStrongRef(mailWindow);
    String path = NSBundle.getMainBundle().getResourcePath();

    if (fileName != null)
    {
        path = path.substring(0, path.lastIndexOf('/')) + "/Library/local/" + fileName;
    }

        MFMailComposeViewControllerDelegate delegate = new MFMailComposeViewControllerDelegate()
        {
            public void mailComposeControllerDidFinish(MFMailComposeViewController controller, MFMailComposeResult result, NSError error) 
            {
                controller.dismissViewController(true, null);
                mailPicker.removeFromParentViewController();
                mailPicker.removeStrongRef(mailWindow);
                mailWindow.setHidden(true);
                mailPicker = null;
            }

            @Override
            public void didFinish(MFMailComposeViewController controller, MFMailComposeResult result, NSError error) 
            {

            }
        };

        mailPicker.setMailComposeDelegate(delegate);
        mailPicker.addStrongRef((ObjCObject) delegate);

        if (fileName != null)
        {
                // attach CSVs here...
        }

        mailPicker.setSubject((subject != null ? subject : "Test Results"));
        mailPicker.setMessageBody((body != null ? body : "Please find attached the latest results data."), false);

        if (MFMailComposeViewController.canSendMail())
        {
            mailWindow.getRootViewController().presentViewController(mailPicker, true, null);
            mailWindow.makeKeyAndVisible();
        }
    }
}

I have this code in place and Eclipse doesn't flag any problems until I attempt a build, whereby I get the following error:

Exception in thread "main" java.lang.NoClassDefFoundError: org/robovm/rt/bro/NativeObject
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at com.mmu.cfa.DesktopMain.main(DesktopMain.java:26)
Caused by: java.lang.ClassNotFoundException: org.robovm.rt.bro.NativeObject
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    ... 49 more

I've added the RoboVM Cocoa Touch Library and the RoboVM Runtime Library to my main project's build path. If I remove these two methods, my project builds without error. Is there something else I need to setup to get this running?

Upvotes: 1

Views: 267

Answers (1)

Tanmay Patil
Tanmay Patil

Reputation: 7057

Problem

In a Libgdx application, the main project is shared by the projects of all supported platforms.

So main project must contain platform independent code.

RoboVM Cocoa Touch Library and RoboVM Runtime Library are platform dependent and would work only on iOS and not on desktop, android, html etc.


Solution

  1. Remove RoboVM Cocoa Touch Library and RoboVM Runtime Library dependencies from main project.
  2. Add those dependencies to the RoboVM project. (If they are not already present.)
  3. Create an interface representing the functionality that you require in the main project.

    public interface EmailHandler {
        public void createMailWindow();
        public void sendEmail(String fileName, String subject, String body);
    }
    
  4. Create an implementation class in the robovm project with the code you have already written.

    public class RobovmEmailHandler implements EmailHandler {
    
        @Override
        public void createMailWindow() {
            // Your code goes here.
        }
    
        @Override
        public void sendEmail(String fileName, String subject, String body) {
            // Your code goes here.
        }
    }
    
  5. Add the EmailHandler parameter to the constructor of ApplicationListener (your main game class in main project).

    public class MyAwesomeGame extends Game {
    
        private EmailHandler emailHandler; // Use it wherever you like.
    
        public MyAwesomeGame(EmailHandler emailHandler) {
            this.emailHandler = emailHandler;
            // Rest of constructor.
        }
    }
    
  6. Supply the implementation argument in RobovmLauncher (main class of robovm project).

    public class RobovmLauncher extends IOSApplication.Delegate {
    
        @Override
        protected IOSApplication createApplication() {
            EmailHandler emailHandler = new RobovmEmailHandler();
            IOSApplicationConfiguration config = new IOSApplicationConfiguration();
            return new IOSApplication(new MyAwesomeGame(emailHandler), config);
        }
    }
    

Note:
Projects of other platforms would start shouting at you to provide an EmailHandler to them. This is natural and expected. You should provide platform specific implementation of the interface for each platform you want to target.

Hope this helps.
Good luck.

Upvotes: 1

Related Questions