I am having some difficulty trying to execute a javascript function from native code. I want a script writer to be able to define a “recipe” in Javascript. It is created by calling a recipe-creating function I expose from native code. The recipe function takes a config dict which expects a recipe name and an action. The action should be a no-parameters, no-return-value function.
Back in native code, as I am processing the config dict, I cannot seem to get a reference to the action function defined. What I actually get back is a NSDictionary with no keys in it.
Thanks for any help.
Javascript
// topLevel is a valid object I expose. It has a module
// function that returns a new module
var module = topLevel.module("Demo - Recipe");
// module has a recipe method that takes a config dict
// and returns a new recipe
module.recipe({
name: "Recipe 1",
action: function() {
topLevel.debug("Hello from Recipe 1!");
}
});
Native code:
@objc public protocol ModuleScriptPluginExports: JSExport {
func recipe(unsafeParameters: AnyObject) -> ModuleScriptPlugin
}
...
public func recipe(unsafeParameters: AnyObject) -> ModuleScriptPlugin {
guard let parameters : [String:AnyObject] = unsafeParameters as? [String: AnyObject] else {
// error, parameters was not the config dict we expected...
return self;
}
guard let name = parameters["name"] as? String else {
// error, there was no name string in the config dict
return self;
}
guard let action = parameters["action"] as? () -> Void else {
// error, action in the config dict was not a () -> Void callback like I expected
// what was it...?
if let unknownAction = parameters["action"] {
// weird, its actually a NSDictionary with no keys!
print("recipe action type unknown. action:\(unknownAction) --> \(unknownAction.dynamicType) ");
}
...
}
Ok I sorted this out. Posting this in case anyone else comes across this.
The problem is in the Swift code where the JSValue is coerced to a dictionary:
parameters : [String:AnyObject] = unsafeParameters as? [String: AnyObject]
which uses toDictionary(), and doing so seems to throw away the function property.
Instead the JSValue should be kept in tact and then valueForProperty is use to get the function which is itself a JSValue.
let actionValue = parameters.valueForProperty("action");
actionValue.callWithArguments(nil);