Search code examples
macosswiftosx-yosemitejavascriptcore

Changing a JSContext-passed Swift object with JavaScriptCore


I have a problem changing an object passed into JavaScriptCore.

Here is my custom object, defining a single String property called testProperty:

import Foundation
import JavaScriptCore

protocol JSCustomObjectExport: JSExport {
    var testProperty: String { get set }
}

class JSCustomObject: NSObject, JSCustomObjectExport {
    var testProperty: String = "ABC"
}

Here is the AppDelegate in which I create a JSContext, pass my custom object to it and run a JS script to change its testProperty from "ABC" to "XYZ". However the testProperty is never changed.

import Cocoa
import JavaScriptCore

class AppDelegate: NSObject, NSApplicationDelegate {

    @IBOutlet var window: NSWindow!
    lazy var context = JSContext()

    func applicationDidFinishLaunching(aNotification: NSNotification?) {
        println("Started")
        var co = JSCustomObject()
        context.globalObject.setValue(co, forProperty: "customObject")
        context.evaluateScript("customObject.testProperty = 'XYZ'")
        println(co.testProperty) // ERROR(?): This prints "ABC" instead of "XYZ"
    }

}

Am I doing something wrong? Shouldn't co.testProperty change?

Btw, this is an OS X app, compiled in XCode 6.1.1 on OSX 10.10.1.


Solution

  • It seems, it requires that the protocol is marked as @objc, and the class has explicit @objc export name.

    I tried the following script in Playground, and it works

    import Foundation
    import JavaScriptCore
    
    @objc // <- HERE
    protocol JSCustomObjectExport: JSExport {
        var testProperty: String { get set }
    }
    
    @objc(JSCustomObject) // <- HERE
    class JSCustomObject: NSObject, JSCustomObjectExport {
        var testProperty: String = "ABC"
    }
    
    var context = JSContext()
    var co = JSCustomObject()
    context.globalObject.setValue(co, forProperty: "customObject")
    context.evaluateScript("customObject.testProperty = 'XYZ'")
    println(co.testProperty) // -> XYZ