WKWebview - Javascript和本机代码之间的复杂通信

Cle*_*rem 27 javascript objective-c ios swift wkwebview

在WKWebView中,我们可以使用webkit消息处理程序调用ObjectiveC/swift代码,例如: webkit.messageHandlers.<handler>.pushMessage(message)

它适用于没有参数的简单javascript函数.但;

  1. 是否可以使用JS回调函数作为参数调用本机代码?
  2. 是否可以从本机代码向JS函数返回一个值?

Cle*_*rem 11

不幸的是我找不到本机解决方案.

但以下解决方法解决了我的问题

使用javascript promises,您可以从iOS代码中调用resolve函数.

UPDATE

这就是你如何使用诺言

在JS中

   this.id = 1;
    this.handlers = {};

    window.onMessageReceive = (handle, error, data) => {
      if (error){
        this.handlers[handle].resolve(data);
      }else{
        this.handlers[handle].reject(data);
      }
      delete this.handlers[handle];
    };
  }

  sendMessage(data) {
    return new Promise((resolve, reject) => {
      const handle = 'm'+ this.id++;
      this.handlers[handle] = { resolve, reject};
      window.webkit.messageHandlers.<yourHandler>.postMessage({data: data, id: handle});
    });
  }
Run Code Online (Sandbox Code Playgroud)

在iOS中

window.onMessageReceive使用适当的处理程序ID 调用该函数

  • 例?"使用Javascript承诺"不是很清楚. (4认同)

小智 9

有一种方法可以使用WkWebView从本机代码中将返回值返回给JS.这是一个小小的黑客,但没有问题我的工作正常,我们的生产应用程序使用了大量的JS/Native通信.

在分配给WKWebView的WKUiDelegate中,重写RunJavaScriptTextInputPanel.这使用委托处理JS提示函数来完成此任务的方式:

    public override void RunJavaScriptTextInputPanel (WebKit.WKWebView webView, string prompt, string defaultText, WebKit.WKFrameInfo frame, Action<string> completionHandler)
    {
        // this is used to pass synchronous messages to the ui (instead of the script handler). This is because the script 
        // handler cannot return a value...
        if (prompt.StartsWith ("type=", StringComparison.CurrentCultureIgnoreCase)) {
            string result = ToUiSynch (prompt);
            completionHandler.Invoke ((result == null) ? "" : result);
        } else {
            // actually run an input panel
            base.RunJavaScriptTextInputPanel (webView, prompt, defaultText, frame, completionHandler);
            //MobApp.DisplayAlert ("EXCEPTION", "Input panel not implemented.");

        }
    }
Run Code Online (Sandbox Code Playgroud)

在我的例子中,我传递数据类型= xyz,name = xyz,data = xyz来传递args.我的ToUiSynch()代码处理请求并始终返回一个字符串,它返回JS作为一个简单的返回值.

在JS中,我只是使用格式化的args字符串调用prompt()函数并获取返回值:

return prompt ("type=" + type + ";name=" + name + ";data=" + (typeof data === "object" ? JSON.stringify ( data ) : data ));
Run Code Online (Sandbox Code Playgroud)


Sas*_*iha 7

这个答案使用了 Nathan Brown上面的答案的想法。

据我所知,目前没有办法将数据返回到 javascript同步方式。希望苹果在未来的版本中提供解决方案。

所以hack就是拦截来自js的提示调用。Apple 提供此功能是为了在 js 调用警报、提示等时显示本机弹出设计。现在因为提示是功能,您可以在其中向用户显示数据(我们将利用它作为方法 param )以及用户对此的响应提示将返回给 js(我们将利用它作为返回数据)

只能返回字符串。这是以同步方式发生的。

我们可以按如下方式实现上述想法:

javascript 端: 通过以下方式调用 swift 方法:

    function callNativeApp(){
    console.log("callNativeApp called");
    try {
        //webkit.messageHandlers.callAppMethodOne.postMessage("Hello from JavaScript");


        var type = "SJbridge";
        var name = "functionOne";
        var data = {name:"abc", role : "dev"}
        var payload = {type: type, functionName: name, data: data};

        var res = prompt(JSON.stringify (payload));

        //{"type":"SJbridge","functionName":"functionOne","data":{"name":"abc","role":"dev"}}
        //res is the response from swift method.

    } catch(err) {
        console.log('The native context does not exist yet');
    }
}
Run Code Online (Sandbox Code Playgroud)

swift/xcode 端执行如下操作:

  1. 实现协议WKUIDelegate,然后将实现分配给 WKWebviewsuiDelegate属性,如下所示:

    self.webView.uiDelegate = self
    
    Run Code Online (Sandbox Code Playgroud)
  2. 现在写这个func webView来覆盖(?)/拦截prompt来自javascript的请求。

    func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
    
    
    if let dataFromString = prompt.data(using: .utf8, allowLossyConversion: false) {
        let payload = JSON(data: dataFromString)
        let type = payload["type"].string!
    
        if (type == "SJbridge") {
    
            let result  = callSwiftMethod(prompt: payload)
            completionHandler(result)
    
        } else {
            AppConstants.log("jsi_", "unhandled prompt")
            completionHandler(defaultText)
        }
    }else {
        AppConstants.log("jsi_", "unhandled prompt")
        completionHandler(defaultText)
    }}
    
    Run Code Online (Sandbox Code Playgroud)

如果你不调用completionHandler()then js 执行将不会继续。现在解析 json 并调用适当的 swift 方法。

    func callSwiftMethod(prompt : JSON) -> String{

    let functionName = prompt["functionName"].string!
    let param = prompt["data"]

    var returnValue = "returnvalue"

    AppConstants.log("jsi_", "functionName: \(functionName) param: \(param)")

    switch functionName {
    case "functionOne":
        returnValue = handleFunctionOne(param: param)
    case "functionTwo":
        returnValue = handleFunctionTwo(param: param)
    default:
        returnValue = "returnvalue";
    }
    return returnValue
}
Run Code Online (Sandbox Code Playgroud)