Reduce boilerplate when sharing code

I’m evaluating using multi-OS engine to share java code between an android and iOS project. There seems to be quite a bit of boilerplate to expose a java class to objective-c/swift. For example, if you have the shared class:

public class Hello {
  public String greet();
}

you need to create a proxy class extending NSObject:

@RegisterOnStartup
@ObjCClassName("Hello")
public class HelloProxy extends NSObject {
  private Hello hello = new Hello();

  protected HelloProxy(Pointer peer) {
    super(peer);
  }

  @Selector("greet")
  public String greet() {
    return hello.greet();
  }
}

as well as manually create the objective-c files:

#import <Foundation/Foundation.h>

@interface Hello : NSObject 

- (NSString*)greet;

@end
#import "Hello.h"

@implementation Hello
@end

This is quite a lot!

I have a few ideas how to reduce this. First, you could get rid of the proxy class by allowing the annotations on the shared java class. For this to work on android, it either needs to not extend NSObject or there has to be some “stub” implementation provided.

Secondly, there could be a way to auto-generate the objective-c files.

To clarify, is that to be able to call Java from ObjC or Swift code?

I solved it in my app by implementing iOS UI all in Java. So, UI controllers call share Lava code and invoke NatJ binding for iOS UI.

Yes, calling java from objective-c/swift. I’d rather take a more conservative approach and write the ui in swift. We already have experienced ios devs and are only looking at ways to share some business logic. It also makes it easier to drop into existing projects.

Just to point out, the iOS UI api would be the same whether you call it from Java or from Swift… But I can see your point in regards to Swift developers.

Hi,

First, you need to define an Objective-C API that will be consumed by your native (ObjC/Swift) code.
Without more information about your app we can’t give you any advice on how to structure this part.

Then you can choose from the following:

  • Implement the interface in Objective-C and then generate bindings for it using the Nat/J Generator
  • Implement the interface in Java and then generate Objective-C stubs for it using the UI binding generator. Note, that this generator is limited to things that are usually needed for UIViewControllers to interact correctly with the Storyboard editor (IBAction and IBOutlet)
  • Do everything by hand, like you proposed
  • Use JNI directly

If you implement the interface in Objective-C, you can choose to implement it as a protocol. Then, Nat/J Gen will create a Java interface for it, that you can implement in your Java wrapper class. This wrapper class does not have to inherit from NSObject or any other Nat/J base class. You still need to have a way to pass a reference to an instance of this wrapper class to the ObjC runtime. We usually do that through a singleton ObjC class with a method like +(void) setInstance:(MyJavaWrapper *) wrapper, and generate the Java bindings for this class as well.

Best Regards,
Gergely

Alright, implementing the interface in Objective-C is an interesting idea, I’ll have to try that out. Any plans to make the UI binding generator more general?

I too am interested in a more “conservative” approach as you put it, i.e. sharing business logic code in Java while writing UI code in ObjC or Swift as usual.

Have you hit upon a better approach for this that involves minimal boilerplate?