How to build an iOS framework?

How to build an iOS framework?

Hello,

Is it possible to create iOS dynamic frameworks with MOE (instead of entire apps)?

Robovm allowed the creation of such frameworks (Embedding RoboVM in Objective-C/Swift Apps) and this is a mandatory feature when you want to provide people with a cross-platform SDK.

Thanks,

Franck

11 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi Franck,

Thank you for your message, We are interested to hear your requirements.

Current version of Multi-OS Engine allows to build only iOS apps and doesn't allow building iOS dynamic frameworks (to be used with other applications written on Obj-C and Swift). 

Alexey

Hi Alexey,

Thanks for your quick answer. 4riders develops a 3D map application for skiers based on LibGDX. We also offer our customers a SDK for creating mobile applications with our 3D maps, available as an AAR library and an iOS dynamic framework.

If we can't build iOS frameworks with MOE, we will have to move to another technology stack (C++ shared library?) or develop twice our framework for Android and iOS...

Franck.

Hi Franck,

We would like to clarify what exactly you are interested in. And what kind of resulted embedded ObjC/Swift frameworks will suite you and your customers. 

RoboVM approach of enabling dynamic framework as output based on using JNI interface. Build system provide you only JNI API and there are no any native ObjC/Swift classes bind with your java code. You are responsible for creating JVM instance, instantiating java classes via JNI, type conversion ObjC <-> Java, manage communication between Java GC and ObjC ARC, and etc. Is it really suite you with your purposes?

This approach doesn’t require a lot of changes in MOE build process, and we can consider including it to one of next MOE updates.

Alexey

Hi Frank,

To understand your requirement better, can you explain your framework:

1) It is written in Java and you provide ObjectiveC API?

2) The ObjectiveC API is hand-written using JNI?

Feel free to send us a private email if you prefer to discuss this offline.

Thanks,

Peng.

 

Hi Peng,

> 1) It is written in Java and you provide ObjectiveC API?

Yes. All our shared (ie. Android & iOS) code is written in Java and we provide an Objective-C API (aka a "Cocoa Touch Framework" or iOS dynamic framework) as well as an Android AAR library. People can then import our framework as an "Embedded Binary" into an iOS project using Xcode (Objective-C or Swift), or use the AAR bundle with Android Studio.

> 2) The ObjectiveC API is hand-written using JNI?

Partly. RoboVM first provided a MyJavaFramework sample project that relies entirely on hand-written JNI calls. Then, they released the improved AnswerMe sample project, which relies on a small JNI initialization class (see init.m) and, as they say in the readme, on "wrapper classes in Java which are exposed as Objective-C classes using the RoboVM Objective-C to Java bridge". I'm using the later mechanism, which is far from perfect, requires to hand-write a lot of tedious code, but works as expected.

@Alexey: being "responsible for creating JVM instance, instantiating java classes via JNI, type conversion ObjC <-> Java, manage communication between Java GC and ObjC ARC, and etc." is NOT a nice solution, but it's better to have a bad solution than no solution at all... Of course, you're free to implement something that would simplify my life a bit ;)

Franck.

 

Best Reply

Dear Franck,

we just released a few samples that show the different options on how to access Java code from Objective-C with MOE. You can get them on Github: https://github.com/migeran/moe-objc-java-samples 

If you want an Objective-C wrapper for a Java library, the best way is to create your empty Objective-C wrapper class(es), then use the "MOE Actions" -> "Generate Bindings" function to create the Java bindings for these wrapper classes. Then you can just go ahead and call your Java library from the Java binding classes, no need to write JNI by hand. The samples above show different strategies to implement these wrappers.

Currently MOE does not support packaging everything into a framework (including the runtime + Java code) for redistribution. We could add the required entry points quite easily: the MOE runtime uses JNI internally for startup, just like RoboVM in the MyJavaFramework samples, and it would work fine: as long as an app only uses a single Java framework.

This is actually a problem with RoboVM's solution as well: if 2-3 or more such frameworks are added to an app, then 2-3 or more VMs will run in parallel in the same app. Even if the VMs itself don't collide and crash when trying to use some global resources, this setup wastes a lot of memory and cpu cycles, because it will have multiple GCs running, the standard Java classes like String will be loaded multiple times ... etc.

Therefore I would like to propose an alternative solution.  

You mentioned, that you are releasing your SDK on Android as an AAR module, and you are reusing the Java code from your Android version with an Objective-C wrapper on iOS.

There are 2 main use cases for your library on iOS:
1. If your client wants to develop using MOE (e.g. to share code between Android and iOS), you can just provide them the Java library in a jar file, that the client can drop in the MOE project, or add it as a Gradle dependency and it will just work.
2. If your client wants to develop in Objective-C / Swift, then your Java library and Objective-C bindings could be distributed as CocoaPods. We would provide special CocoaPod recipes for such Java libraries and their dependencies, so the end user only have to specify the pods in her project configuration, and the build will automatically combine the different Java libraries, minify them using proguard and add the MOE runtime as a framework dependency to the project. The end user only has to add a single line (MOE_init(argc, argv);) in the main.m, before UIApplicationMain() is called. This will initialize the MOE VM and set up its connection with the Objective-C runtime. After that the Java library can be accessed through the Objective-C wrappers, without the need of JNI calls.

The advantages of this proposal are:
- You can provide a way to your users to share code between Android and iOS
- Works well with others: if multiple Java libraries are used in an iOS app, only a single VM is required and the libraries are minified (Proguarded) together for maximum benefits.
- CocoaPods provides a nice dependency management mechanism for other external libraries / frameworks, not just MOE ones
- The setup code can be branded, so you can tell the users to add the YourSDK_init(argc, argv); call to the main.m, and from your init method you just have to call MOE_init(argc, argv); MOE_init will make sure that it only initializes once, even if it is called multiple times.

What do you think of this proposal? Would these distribution methods be sufficient for your purposes?

Please let me know, if you have any questions.

Best Regards,
Gergely Kis
 

 

Hi Gergely,

Many thanks for this detailed answer! Your proposal sounds very good, as well as the distribution methods (I didn't think at first I could suggest our customers to develop with MOE, but this is an interesting option).

Unfortunately, I couldn't try your sample project: the MOE installer complains I don't have a version 1.2 or higher of Android Studio, I suspect something is wrong with the new Android Studio 2.0...

Anyway, MOE sounds more and more like a very good option for what we are trying to do.

Franck.

Hi Franck,

Multi-OS Engine installer throws an error message "Android Studio version 1.2 or higher is required" on Mac OS X with installed Android Studio 2.0 released recently. To workaround this error, please open "/Applications/Android Studio.app/Contents/Info.plist" file in any text editor, find "CFBundleShortVersionString" value and replace 2.0 with 2.1 there:

<key>CFBundleShortVersionString</key>
<string>2.1</string>

Then install the Multi-OS Engine and replace the version back to 2.0 in the Info.plist file. We will release updated Multi-OS Engine package with fix for this issue soon.

Alexey

Hi,

I just tried the https://github.com/migeran/moe-objc-java-samples projects with Android Studio 2.1 and everything works fine (no workaround required ;)

The objc code in the JNIFromObjc sample project is very verbose but the CounterService.java implementation doesn't need to extend NSObject, which is good because the objc code can call directly our shared code (having a dependency to MOE with the Keep annotation isn't a big issue). On the other hand, the ImplObjCClassInJava* samples require to generate and maintain a full layer of wrappers, but the objc code is much easier to implement.

Would it be possible to have the best of both world (very naive question I guess)?

Franck.

Quote:

Gergely Kis - Migeran Ltd wrote:

2. If your client wants to develop in Objective-C / Swift, then your Java library and Objective-C bindings could be distributed as CocoaPods. We would provide special CocoaPod recipes for such Java libraries and their dependencies, so the end user only have to specify the pods in her project configuration, and the build will automatically combine the different Java libraries, minify them using proguard and add the MOE runtime as a framework dependency to the project. The end user only has to add a single line (MOE_init(argc, argv);) in the main.m, before UIApplicationMain() is called. This will initialize the MOE VM and set up its connection with the Objective-C runtime. After that the Java library can be accessed through the Objective-C wrappers, without the need of JNI calls.

I'm curious if any more thought has been put into this. I think the largest barrier to introducing shared code between our iOS and Android teams is that the iOS team would have to switch to a MOE project. Being able to distribute a library as a pod would let many orgs start using MOE without fully switching over their iOS team.

Leave a Comment

Please sign in to add a comment. Not a member? Join today