How to create a Java library that can be used in other non-MOE Xcode projects

I’m still a bit confused. My team’s goal is to create a common Java library to be used by both our iOS and Android teams. Do the new features in MOE 1.3 make this possible? If so, is there (or will there be) any documentation explaining how to set up such a project?

@kisg

I see that you have split my question from the other thread, but my question is not about using a common Java library with MOE; I am wondering how to go about creating such a library with MOE and building it as a framework for an iOS consumer in another non-MOE Xcode project. This very much relates to the previous topic.

Hi Damian,

I moved your question to its own topic because it is separate from the other topic.

Using a common Java library on both iOS and Android is one of the main use cases of MOE. If you look at LibGDX for example, this is how it works: most of the app code is shared across platforms.

We also have non-libGDX samples e.g. our basic Calculator sample, that shows how you can share a common module / library that is written in Java (or any other JVM language) between Android and iOS.

The other topic was about using native iOS libraries from Java with MOE.

Best Regards,
Gergely

Hi Damian,

ok, I misunderstood your question. So you want to create a Java library that is accessible from Objective-C / Swift in another project?

I will write more details about this in a minute.

Best Regards,
Gergely

Yes, that’s the goal. Are library projects possible, and can they be used to build for both iOS (framework/pod) and Android (JAR/AAR) targets?

There are multiple facets to this question:

  1. We don’t have a special “MOE library project” that is the cross-platform equivalent of Android AAR. However, you can build cross-platform JAR projects, that you can pull in as a dependency into your Android and your MOE project.
  2. Using this cross-platform Java library in a non-MOE Xcode project is not possible: you will need some changes to the Xcode project to call into the Java build process, to link with the MOE framework, and also add the special linker flags to link in the compiled Java code.
  3. However, one of the main goals of MOE 1.3 was to make the above process as painless as possible. In my opinion MOE 1.3 is less intrusive on an Xcode project than CocoaPods.

To add MOE 1.3 (and your Java library) to an Xcode project, you have to follow these steps:

  1. Create a new MOE project (e.g. with your Java IDE’s New MOE Project)
  2. Replace the generated Xcode project in the MOE project with your existing project. If your project uses Xcode workspaces, you will need to adjust the build.gradle file accordingly, in particular set the following values:
moe {
     xcode {
         project 'path/to/main.xcodeproj'
         mainTarget '<Main Target>'
         testTarget '<Test Target>'
         // New values added
         workspace 'path/to/xcworkspace'
         mainScheme '<Main Scheme Name>'
         testScheme '<Test Scheme Name>'
     }
}
  1. Open the adjusted MOE project in your Java IDE and run the Multi-OS Engine Actions > Inject/Refresh Xcode Project Settings action to add the MOE specific changes to the your Xcode project. This is the equivalent of pod install. (In case of using a workspace with multiple projects this must be the main project e.g. the one where you ran pod install).
  2. You need to initialize MOE VM on application startup. You should replace the usual UIApplicationMain() call in your main method with a call to moevm() (see the Xcode project that is generated by MOE).
  3. You need to add the MOE.Main.Class property to your App’s Info.plist. This property contains a fully qualified Java class name that has a static main() method. This main method will be called by moevm() after the VM startup is complete.
  4. You can keep this main class very simple, e.g. just call UIApplicationMain with the name of your UIApplicationDelegate class implemented in ObjC or Swift:
   public class Main {
      public static void main(String[] args) {
          UIKit.UIApplicationMain(0, null, null, "YourAppDelegateWrittenInObjC");
      }
  }
  1. Add your common library to your build.gradle as a dependency. Also add the appropriate rules in proguard.append.cfg so your library is not thrown out by ProGuard.

At this point you can just click Run in Xcode and it will correctly build your project (including the Java library) and run it. Your common Java code will be in your app, but now you need to expose it somehow to Objective-C / Swift.

There are different strategies on how to do this. We created a few sample projects for the different options. These samples were created for an older version of MOE, and we have not yet got around to including them in the official samples, but you can check them out here. The concept is pretty much the same with the current MOE as well.

Once this process is done, you can commit the code to Git, and a team member can check it out, and use it as a native Xcode project. He will only need to have the JDK installed, but everything else (Gradle, MOE SDK) will be downloaded automatically. He can work from Xcode on the ObjC / Swift code, and does not need to touch a Java IDE (if he does not want to :slight_smile: ).

A few notes about possible future improvement to this process:

  1. The process is pretty straightforward up to setting up the Java main class that can then redirect the control to the native UIApplicationDelegate. We considered adding a separate entry point called moevm_init() that initializes the VM, but does not call the main method, and returns. Then, one could call the usual UIApplicationMain without creating a dummy Main Java class.
  2. MOE 1.3 also includes limited Objective-C stub generation support from Java code. It is used for generating Outlet and Action stubs for use with Storyboards, but the concept could be extended to work like the @objc annotation in Swift for any Java class. We would then package the necessary Nat/J annotations in a separate jar, that would not have any iOS dependencies, so it would be safe to include in a standard Android project.

Hope this helps.

Hi again @kisg,

Thanks a bunch for your help. I’ve been trying what you suggested, but am unable to get past step 3 (Inject/Refresh Xcode Project Settings) - whenever I try it, I get the following message:

Do you have any idea what the problem might be? If you need more details on the Xcode project, I will have to ask our iOS developer since I’m not too familiar with the specifics.

I’ve tried continuing past this regardless, but the import for MOE/MOE.h is not found anyway.

Thanks again and let me know if you have any ideas,
Damian

Update:

I managed to get past that problem by changing testTarget to the same as mainTarget. However, I still am unable to build the project - I am getting an error that 'MOE_PROJECT_DIR' doesn't point to a directory!. Looking at the environment variables, it appears that the source set is being detected as test instead of main, and it’s looking in build/moe/test instead of build/moe/main. Is this a bug, or is there some way to fix this? If I manually change the value in the pbxproj file, it fails with MOE.h not being found when building from IDEA, but Xcode still throws the directory error.

Thanks again,
Damian

It is hard to help without seeing the actual project. That said: If your build.gradle is configured correctly, then before you try to do the injection of MOE, you should be able to create a Run configuration from your Java IDE and run it. This should work because during launch we just use xcodebuild internally to build the configured target, then just install/launch the created App. We don’t check, if it is actually a MOE-enabled project.

So if this works, then it might be a bug in our injection code. If it doesn’t, then it looks like the build.gradle file does not match your Xcode project.

If you can’t get it to work, please share your Xcode project, so we can try to reproduce this error.