2

The following piece of code works like a charm to define a function in Swift (2.0) that I can call from a Javascript resource (tvos). The function storeSetPackageInfo accepts a parameter and returns nothing.

I am trying to understand how I achieve the same goal with a function that accept no parameters and returns a boolean. I don't seem to understand the syntax.

private typealias JavascriptClosure = (JSContext) -> Void
private typealias ObjectivecCompletionBlock = @convention(block) (String) -> Void

func setupStoreSetPackageInfo() {
        let selectComponent: JavascriptClosure = {
            [unowned self](context: JSContext) -> Void in
            let objCompletion: ObjectivecCompletionBlock = {
                (str: String) -> Void in
                (self.delegate as? myTVAppControllerDelegate)?.storeSetPackageInfo(str)
            }
            context.setObject(unsafeBitCast(objCompletion, AnyObject.self), forKeyedSubscript: "storeSetPackageInfo")
        }
        evaluateInJavaScriptContext(selectComponent, completion: nil)
    }

I tried multiple approaches which compile but resulting in the JSContext in not finding the function. Any help is very appreciated.

amok
  • 1,736
  • 4
  • 20
  • 41

2 Answers2

1

I described one possible way just yesterday in another context: How to retrieve values from settings.bundle in TVML?

AppDelegate.swift

func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
    let jsInterface: cJsInterface = cJsInterface();
    jsContext.setObject(jsInterface, forKeyedSubscript: "swiftInterface")
}

JsInterface.swift

@objc protocol jsInterfaceProtocol : JSExport {
    func getSetting(setting: String) -> String
}
class cJsInterface: NSObject, jsInterfaceProtocol {
    func getSetting(setting: String) -> String {
        return "<yourSetting>"
    }
}

on the JS side...

swiftInterface.getSetting(...)

It's definitely a different syntax compared to your example, but known to work. See https://github.com/iBaa/PlexConnectApp.

Community
  • 1
  • 1
Baa
  • 246
  • 1
  • 8
  • Can this work some way with WKWebView? It seems that there is no way to call swift method from javascript and synchronously get value. Apple support only postMessage without returning value. In Android it is possible. – mikep Feb 07 '21 at 15:19
0

After multiple attempts, I found it, the answer and the solution was in front of me at all time... I had tried before but I eventually I had other messy attempts around. To benefit whoever runs into this problems, here is the solution for any signature

private typealias ObjectivecCompletionBlock = @convention(block) () -> Bool

the completion block must match the signature with

() -> Bool in

Therefore the final code is

private typealias JavascriptClosure = (JSContext) -> Void
private typealias ObjectivecCompletionBlock = @convention(block) () -> Bool
func setupStoreSetPackageInfo() {
        let selectComponent: JavascriptClosure = {
            [unowned self](context: JSContext) -> Void in
            let objCompletion: ObjectivecCompletionBlock = {
                () -> Bool in
                (self.delegate as? myTVAppControllerDelegate)?.storeSetPackageInfo(str)
            }
            context.setObject(unsafeBitCast(objCompletion, AnyObject.self), forKeyedSubscript: "storeSetPackageInfo")
        }
        evaluateInJavaScriptContext(selectComponent, completion: nil)
    }

Again really straightforward (once you pull the head out of the bucket...)

amok
  • 1,736
  • 4
  • 20
  • 41