1

I want to use the Async functionality offered by WKWebView outside web view. The JS Context option does not provide the Async functionality.

In WKWebView, I write my logic as follows.

func swiftFunc1() {
   webView.evaluateJavaScript("jsFunc1(), completionHandler: nil)
}

In javascript code I post a message to swift

function jsFunc1() {
    window.webkit.messageHandlers.myMsg.postMessage("call swiftFunc2");
}

The swift code can then call appropriate JS callback as a part of handling message.

But this is dependent upon the webview being the foreground view. If I want to use the JS logic independent of the webview, JSContext is the option. I tried following

func swiftFunc1() {
    myCtxt = JSContext()
    exportedToJS = exportToJS() //confirms to JSExport and uses @objc
    myCtxt.setObject(exportedToJS.self, forKeyedSubscript: "swiftIface")
    myFunc = myCtxt.objectForKeyedSubscript("jsFunc1")
    myFunc.callWithArguments(nil)
}

Now in javascript code I cannot post a message to swift. If I try to call a swift function as follows, code gets stuck forever.

function jsFunc1() {
   swiftIface.swiftFunc2() // This creates a deadklock
 }

How can I achieve either of the following without "returning" from the called Javascript function jsFunc1()?

  1. Either post a message to swift so that it can take appropriate action

  2. Or call a swift function so that the appropriate action is taken

1 Answer 1

2

Do I understand you right, if you do not want your javscript to terminate after execution?

If I understood you wrong, maybe following helps (I am not at home at Mac to test, but maybe it works if you modify your code as follows).

Option 1: Blocks

The swiftFunc1 could look like this:

func swiftFunc1() {
    myCtxt = JSContext()
    myCtxt.setObject(unsafeBitCast(swiftFunc2, AnyObject.self), forKeyedSubscript: "swiftFunc2")
    exportedToJS = exportToJS() //confirms to JSExport and uses @objc
    myCtxt.evaluateScript("swiftFunc2()")
}

Your swiftFunc2 would look like this:

let swiftFunc2: @convention(block) Void -> Void = { 
  // do something here
}

Your JS code would look like this:

function jsFunc1() {
   swiftFunc2();
 }

Option 2: JSExport Your have an exported class which is accessible for all javascript:

import Foundation
import JavaScriptCore
@objc class JavascriptHandler: NSObject, JavascriptHandlerExport {
  let context: JSContext = JSContext()

  init () {
    context.setObject(self, forKeyedSubscript: "MyJSHandler") // set the object name for self accessible in javascript code
  }
  func swiftFunc1() {
    context.evaluateScript("MyJSHandler.swiftFunc2();")
  }
  func swiftFunc2 () {
    // do something here
  }
}

Your protocol for the exported class.Here you have to declare all properties and methods you want to use with Javascript.

import Foundation
import JavaScriptCore
@objc protocol JavascriptHandlerExport: JSExport {
  func swiftFunc2 ( ) -> Void
}

With this it should be possible for you to call a function from javascript and still let it continue. You can now access the functions of the class JavascriptHandler from Javascript like this in this example:

MyJSHandler.swiftFunc2();

If you want to seperate the class where your WebView is from the one where your JS logic lies that should also not be a problem. You should also be able to combine the block syntax with the JSExport method.

Let me know if it's not working/behaving as wanted.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the answer. But does this mean that I should only be using blocks/closures to get around this? JSExport can not handle this kind of call sequence, is it?
It should be possible too with JSExport. Maybe if I have time tomorrow I can try to give an example.
Looking forward to it!
Could you find this possible?
I have tried this kind of arrangement previously and found that XCode simply hangs when there is a cycle like this. See stackoverflow.com/questions/37908059/…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.