Call to swift method from JavaScript hangs xcode and application

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-23 09:18:03

问题


I am writing an iOS App (using xcode 7.3 and swift 2.2) using JavascriptCode framework. Calling javascript methods from swift works perfect, but when I call the swift method from javascript, xcode simply shows a "loading" type of symbol and nothing happens. I need to "force quit" xcode to get out of this state. I have followed https://www.raywenderlich.com/124075/javascriptcore-tutorial and http://nshipster.com/javascriptcore/ and I am trying pretty simple calls.

Has anyone faced this kind of issue?

My swift code is as follows:

@objc protocol WindowJSExports : JSExport {
   var name: String { get set }
   func getName() -> String
   static func createWindowWithName(name: String) -> WindowJS
}

@objc class WindowJS : NSObject, WindowJSExports {
   dynamic var name: String
   init(name: String) {
       self.name = name
   }    
   class func createWindowWithName(name: String) -> WindowJS {
       return WindowJS(name: name)
   }    
   func getName() -> String {
       NSLog("getName called from JS context")
       return "\(name)"
   }
} 

I am initializing the context as follows:

runContext = JSContext()
runContext.name = "test_Context"

windowToJs = WindowJS(name: "test")
runContext.setObject(windowToJs.self, forKeyedSubscript: "WindowJS")

If I replace the last two lines in above code with below code without instantiating it, the code simply fails to load.

runContext.setObject(WindowJS.self, forKeyedSubscript: "WindowJS")

And the javascript code is as simple as

function check() {
    return WindowJS.getName()
}

I do see the breakpoint being hit in the JS function check and when the WindowJS.getName gets called, xcode simply becomes unresponsive.


回答1:


You're creating a deadlock since you are calling from Swift to JavaScript back to Swift. I'm not sure exactly why it is a deadlock but I had a similar issue with WKWebView on Mac recently.

You need to decouple this and make the communication asynchronous. This obviously means you cannot simply return a value from your JS function in this case.

To decouple, you can break the deadlock by deferring the work the JavaScript function needs to do out of the current runloop iteration using setTimeout:

function myFunction() {
  setTimeout(function() {
    // The actual work is done here.
    // Call the Swift part here.
  }, 0);
}

The whole native ↔︎ JavaScript communication is very, very tricky. Avoid it if you can. There's a project called XWebView that may be able to help you as it tries to ease bridging between the two worlds.




回答2:


The setTimeout could be solved by adding following piece of code to my swift function.

let setTimeout: @convention(block) (JSValue, Int) -> () = 
{ callback, timeout in
    let timeVal = Int64(timeout)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timeVal), dispatch_get_main_queue(), { callback.callWithArguments(nil)})
}

To expose this native code to the JS context, I also added following.

runContext.setObject(unsafeBitCast(setTimeout, AnyObject.self), forKeyedSubscript: "setTimeout")

Things then worked fine.



来源:https://stackoverflow.com/questions/37908059/call-to-swift-method-from-javascript-hangs-xcode-and-application

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!