《成为大前端》系列 4.5 Native与JS通信-参数传递和结果返回(iOS)

1,579 阅读2分钟

JS 传递参数到 Native

前面完成了 JS 调用 Native,接下来继续 JS 如何传递参数到 Native

传递原始类型数据

先看 JS 端的代码:

function onClickButton() {
  window.webkit.messageHandlers.iOSBridge.postMessage("Hello");
}

postMessage时传递了Hello字符串,再看 Native 如何接收:

func userContentController(
        _ userContentController: WKUserContentController,
        didReceive message: WKScriptMessage)
{
    // postMessage传递的参数可以通过message.body拿到,但需要转换成Swift的String
    let body = message.body as! String

    // 打印一下
    print("WebView callNative ok. body is \(body)")
}

运行后,打印的结果:

WebView callNative ok. body is Hello

传递 json 对象

window.webkit.messageHandlers.iOSBridge.postMessage({
  name: "mingo"
});

Native:

// 将body转换为Swift的字典结构
let body = message.body as! [String : Any]
print("WebView callNative ok. body is \(body)")
print("WebView arg name=\(body["name"]!)")

输出:

WebView callNative ok. body is ["name": mingo]
WebView arg name=mingo

JS 传递参数到 Native 只能传递原始数据类型,JSON 里也只能包含原始数据类型 Function/Class 之类的是不能传递到 Native 的,因此他们之间实际是信息交 换,而不是同一语言的调用关系。

Native 返回结果给 JS

首先要明确的是:Native 没有任何方式“直接返回”结果给 JS,至少 iOS 的 WKWebView 是这样的。 但是 Native 可以运行一段 JS 代码:

webView.evaluateJavaScript("console.log('log from native')") { _, _ in }

我们通过 webView 的evaluateJavaScript方法,运行了 JS:

console.log("log from native");

所以我们利用这个功能实现结果返回,来看看 Native 怎么写:

class ViewController: UIViewController, WKUIDelegate {

    var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = WKWebViewConfiguration()

        // 声明变量,而不是直接在下一行创建
        let birdge = BridgeHandler()
        config.userContentController.add(birdge, name: "iOSBridge")

        webView = WKWebView(frame: self.view.frame, configuration: config)
        webView.uiDelegate = self
        self.view.addSubview(webView)

        // 赋值webView给BridgeHandler
        birdge.webView = webView

        let url = Bundle.main.url(forResource: "test", withExtension: "html")!
        webView.loadFileURL(url, allowingReadAccessTo: url)
    }
}

class BridgeHandler : NSObject, WKScriptMessageHandler {

    var webView: WKWebView!

    func userContentController(
        _ userContentController: WKUserContentController,
        didReceive message: WKScriptMessage)
    {
        let body = message.body as! [String: Any]
        print("WebView callNative ok. body is \(body)")
        print("WebView arg name=\(body["name"]!)")

        // 在收到js调用后运行以下代码
        webView.evaluateJavaScript("onNativeResult('Native callback ok.')") { _, _ in }
    }
}

JS 端做好接收返回的代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <style type="text/css">
      html,
      body {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
      }
      button {
        display: block;
        width: 100%;
        height: 30%;
        font-size: 50px;
      }
      /* 字体放大点 */
      pre {
        font-size: 40px;
      }
    </style>
  </head>
  <body>
    <script type="text/javascript">
      function onClickButton() {
        window.webkit.messageHandlers.iOSBridge.postMessage({
          name: "mingo"
        });
      }

      // 声明一个方法给Native运行
      function onNativeResult(result) {
        var logEl = document.getElementById("log");
        logEl.innerText += result + "\n";
      }
    </script>

    <!-- 打算点击按钮调用native -->
    <button onclick="onClickButton()">Call Native</button>

    <pre id="log">
<!-- 用于显示js的日子 -->
</pre>
  </body>
</html>

运行后,点击几次按钮得到的结果:

到这里已经完成了 Native 返回结果给 JS 端了。