iosobjective-cjavascriptcore

Can't JSExport an Objective-C Method With More Than One Parameter?


Consider this:

@protocol FooExport <JSExport>
- (void)method1:(NSString *)param1;
- (void)method2:(NSString *)param1 param2:(NSString *)param2;
@end

@interface Foo : NSObject <FooExport>
@end

@implementation Foo
- (void)method1:(NSString *)param1 {
    NSLog(@"method1");
}
- (void)method2:(NSString *)param1 param2:(NSString *)param2 {
    NSLog(@"method2");
}
@end

{
    sContext = [[JSContext alloc] init];
    if (sContext)
    {
        sContext[@"foo"] = [[Foo alloc] init];
        [sContext evaluateScript:@"foo.method1(\"foo\");"]; // method1 is called
        [sContext evaluateScript:@"foo.method2(\"foo\", \"bar\");"]; // method2 is NOT called
    }
}

method1 is called just fine but method2 is never called.

If I change method2 as follows:

@protocol FooExport <JSExport>
- (void)method1:(NSString *)param1;
- (void)method2:(NSString *)param1;
@end

method2 is now called via [sContext evaluateScript:@"foo.method2(\"foo\", \"bar\");"]; (and I have to dig out the second parameter via JSContext.currentArguments).

Similarly, if I change method2 as follows:

@protocol FooExport - (void)method1:(NSString *)param1; - (void)method2; @end

method2 is again called via [sContext evaluateScript:@"foo.method2(\"foo\", \"bar\");"]; (and I have to dig out both parameters via JSContext.currentArguments).

Is this by design? The drawback of JSContext.currentArguments is that I have to deal with JSValues rather than the already converted objective-C types.


Solution

  • Quickly perusing JSContext.h uncovers this gem:

    // When a selector that takes one or more arguments is converted to a JavaScript
    // property name, by default a property name will be generated by performing the
    // following conversion:
    //  - All colons are removed from the selector
    //  - Any lowercase letter that had followed a colon will be capitalized.
    // Under the default conversion a selector "doFoo:withBar:" will be exported as
    // "doFooWithBar". The default conversion may be overriden using the JSExportAs
    // macro, for example to export a method "doFoo:withBar:" as "doFoo":
    //
    //    @protocol MyClassJavaScriptMethods <JSExport>
    //    JSExportAs(doFoo,
    //    - (void)doFoo:(id)foo withBar:(id)bar
    //    );
    //    @end
    //
    // Note that the JSExport macro may only be applied to a selector that takes one
    // or more argument.
    #define JSExportAs(PropertyName, Selector) \
        @optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector