Use a NSBlock as a property in JavaScriptCore

438 views Asked by At

I need to use a NSBlock as a property that can be changed in JavaScript.

@protocol ExportingUser <NSObject, JSExport>

@property (nonatomic, copy) void (^changedName) (void);

@property (nonatomic) NSString* name;

@end

I basically want to call changedName from Obj-C, a function whose definition comes from JavaScript. Like this:

user.name = "Peyman"; // this will change the name property in the Obj-C as well.

user.changedName = function() { console.log("yay"); } // but this doesn't

I know that this is possible through JSValue's callWithArguments method but I prefer this method if possible.

Edit: actually, I don't think the callWithArguments method would work. It's possible since objects that are bound to Objective-C classes are not extensible in JavaScript so introducing new fields would simply be ignored.

1

There are 1 answers

0
IvanAtBest On BEST ANSWER

As listed in JSValue.h :

//   Objective-C type  |   JavaScript type
// --------------------+---------------------
//         nil         |     undefined
//        NSNull       |        null
//       NSString      |       string
//       NSNumber      |   number, boolean
//     NSDictionary    |   Object object
//       NSArray       |    Array object
//        NSDate       |     Date object
//       NSBlock *     |   Function object *
//          id **      |   Wrapper object **
//        Class ***    | Constructor object ***
//
// * Instances of NSBlock with supported arguments types will be presented to
// JavaScript as a callable Function object. For more information on supported
// argument types see JSExport.h. If a JavaScript Function originating from an
// Objective-C block is converted back to an Objective-C object the block will
// be returned. All other JavaScript functions will be converted in the same
// manner as a JavaScript object of type Object.

Apparently, you can get away with :

@property (nonatomic, copy) JSValue* changedName;

Then, when you want to call it:

typedef void (^nameChangedBlock)(void);

nameChangedObject = [self.changedName toObject];

if ([nameChangedObject isKindOfClass: NSClassFromString(@"NSBlock")]){
   ((nameChangedBlock)nameChangedObject)();
}