iOS Native和H5交互

1. WKWebView

1.1 H5调Native(H5向Native传递消息)

1.1.1 H5调Native_直接拦截URL方式

顾名思义就是在WebView加载URL的时候,读出URL字符串,看看里面有没有你和H5特别约定的标记,如果有说明H5想向你传递消息,就干H5让你干的事,忽略这个URL的定向跳转。
拦截方法在下面回调:

webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)

H5那边的代码
就是重定向当前页面URL。就是改变了location.href的值。这里举例子标记是bridge://doingSomething,传参{parma1=sss,parma2=dy}

 h5AskNativeDoSomething1(){
    location.href = "bridge://doingSomething&parma1=sss&parma2=dy"
 }

Native代码
发现URL中包含bridge://doingSomething,可以判断H5要让Native干事情,那么就decisionHandler(.cancel)忽略这个URL的跳转,取出对应参数干事情

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        
        let changeURLString:String = navigationAction.request.url?.absoluteString ?? "未知"
        print("要跳转的URL:\(changeURLString)")
        
        /***方法1 拦截关键标识 bridge://doingSomething,根据自己和H5定了规则,去判断取相应的参数 ***/
        
        //方法1:js调用native
        if changeURLString.contains("bridge://doingSomething"){
            decisionHandler(.cancel)
            
            let list:[String] = changeURLString.split(separator: "&").compactMap { "\($0)" }
            print(list)
            //TODO
        }else{
            decisionHandler(.allow)
        }
    }
1.1.2 H5调Native_WKScriptMessageHandler 回调方式

这是系统JavaScriptCore提供的功能,用的时候需要引用类库import JavaScriptCore

H5那边代码:
Native那边通过WKScriptMessageHandler注册的js对象暴露出来,这边想向原生传递消息,固定写法:window.webkit.messageHandlers.(JS对象XXXX字符串).postMessage({})
其中 window.webkit.messageHandlers 都是固定写法,.postMessage({})也是固定写法。这里例子完整写法就是
window.webkit.messageHandlers.DIYJSBridge.postMessage({})
记住,如果没有参数传递,({})小括号里面的大括号也不能省略,如果省略{},native那边会无响应!如果有参数传递,那么{}里面就是键值对,如:{"param1":1,"param2":2}

/**
* native那边通过WKScriptMessageHandler注册的js对象暴露出来,这边想向原生传递消息,固定写法:window.webkit.messageHandlers.原生那边暴露的js对象XXXX.postMessage({})
* 其中 window.webkit.messageHandlers 都是固定写法,.postMessage({})也是固定写法。记住,如果没有参数传递,({})小括号里面的大括号也不能省略,如果省略{},native那边会无响应
*/
h5AskNativeDoSomething2(){
   window.webkit.messageHandlers.DIYJSBridge.postMessage({})
}

Navtive代码:
我们在WKWebView初始化的时候,给它配置WKWebViewConfiguration的里面添加一个类似监听的东西:

let configuration:WKWebViewConfiguration = WKWebViewConfiguration()

//加监听
let wkUserContnetController:WKUserContentController = WKUserContentController.init()
wkUserContnetController.add(self, name: "DIYJSBridge")//加监听
configuration.userContentController = wkUserContnetController

webView = WKWebView.init(frame:self.view.bounds, configuration:configuration)
webView.scrollView.decelerationRate = UIScrollView.DecelerationRate.normal
webView.navigationDelegate = self
self.view.addSubview(webView)

wkUserContnetController.add(self, name: "DIYJSBridge")这里给self添加一个监听,监听到DIYJSBridge这个JS对象发送的消息(先这么理解)。
然后实现WKScriptMessageHandler 协议的方法:

//MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
   print("方法2:\(message.name)")
   print("方法2:\(message.body)")
}

这里面可以根据WKScriptMessage 读到方法的名称name和参数body,然后就可以干事情。

1.1.3 H5调Native_三方库 WebViewJavascriptBridge

这个库本质还是拦截URL,只是进行了封装,让方法定义和传参更加对象化。具体源码,有机会再写篇文章解析一下!

H5那边的代码:
H5那边需要在index.html定义一个方法,并调用。挂载对应的JS对象,完成相应的配置初始化。

//初始化方法
function setupWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
    if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
    window.WVJBCallbacks = [callback];
    var WVJBIframe = document.createElement('iframe');
    WVJBIframe.style.display = 'none';
    WVJBIframe.src = 'https://__bridge_loaded__';
    document.documentElement.appendChild(WVJBIframe);
    setTimeout(function() { document.documentElement.removeChild(WVJBIframe); }, 0);
}

//调用完成配置初始化
setupWebViewJavascriptBridge((bridge) => {});

现在H5想调用Native方法

h5AskNativeDoSomething3(){
  this.toast("方法3:H5调用Native")     
  window.WebViewJavascriptBridge.callHandler('NativeToDingSomething',{"Pa1":"11111"}, function responseCallback(responseData) {
         console.log(responseData)
  })
}

window.WebViewJavascriptBridge.callHandler()固定写法,其中参数

第一个参数:Native那边定义标记字符串,如:`NativeToDingSomething`
第二个参数:传参,为{}括起来的键值对,如:`{"Pa1":"11111"}`
第三个参数:回调函数,如果Native那边实现(下面Native实现了)了对应的回调Block,所带的回调参数会在这个responseData里面

Native代码:

Native这边引入了三方库WebViewJavascriptBridge,如后导入。
代码初始化一个WebViewJavascriptBridge实例,并关联上WebView

/**方法3 通过三方类库**/
self.webViewBridge = WebViewJavascriptBridge.init(webView)
self.webViewBridge.setWebViewDelegate(self)

这时设置注册一个标记NativeToDingSomething,发现H5 callHandler这个标记后干事情....

self.webViewBridge.registerHandler("NativeToDingSomething") { (ret:Any?, callBack:WVJBResponseCallback?) in
   print(ret)
   //TODO
  if callBack != nil{
     callBack!(["result":true,"data":["One","Two","Three"]])
  }
}

这样H5那边如果触发了

window.WebViewJavascriptBridge.callHandler('NativeToDingSomething',{"Pa1":"11111"}, function responseCallback(responseData) {
        console.log("Native传递过来的参数:",responseData)
  })
}

Native这边收到消息会打印

Optional({
    Pa1 = 11111;
})

同时Native这边执行了回调callBack!(["result":true,"data":["One","Two","Three"]])

H5那边的控制台会打印:


image.png

这样就完成了一次H5发起的双向通信!

1.2 Native调H5(Native向H5传递消息)

1.2.1 Native调H5_直接WebView调用evaluateJavaScript方法
open func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)

如:

self.webView.evaluateJavaScript("nativeAskH5DoSomething('小孩')") { (any:Any?, err:Error?) in
  if err == nil{
    print("调用成功,返回结果\(any)")
  }else{
    print("调用失败\(err)")
  }
}

其中nativeAskH5DoSomething('小孩')是H5定义的js方法

window.nativeAskH5DoSomething = function(mess){
    console.log(mess)
    return "我们都是好孩子"
}

注意:这种方法调用的js方法,必须挂载在Web的window对象上,不然会找不到!
evaluateJavaScript去调用h5中的方法之后,这个function要执行完成之后才会回调的app这边,如果这个function中报错了,回调到app这边就会一直报错。所以建议function中的东西可以先延迟执行。

1.2.2 Native调H5_通过 WebViewJavascriptBridge三方库

这个H5调Native反过来相当于H5注册一个标记,Native去调用。

H5代码,注册方法标识

window.WebViewJavascriptBridge.registerHandler("nativeAskH5DoSomething_WebViewJavascriptBridge",(data,callBack) => {
    this.toast("原生调用我了")
    console.log("原生调用我了")
    console.log("传入参数:",data)
    callBack({"p1":1,"p2":90})
})

第一个参数data:表示原生传入的参数
第二个参数callBack:是回调方法,执行后可穿参数给原生,告诉执行情况

Native代码 事件触发执行

self.webViewBridge.callHandler("nativeAskH5DoSomething_WebViewJavascriptBridge", data: "10086") { (ret:Any?) in
      print("H5传递回来的参数结果:\(ret)")
}

执行结果:
H5打印结果

image.png

Native打印结果

H5传递回来的参数结果:Optional({
    p1 = 1;
    p2 = 90;
})

这样也完成了一次Native发起的双向通信!

2. UIWebView

H5调Native: 1.1.1,1.1.3 和WKWebView相同!
Native调H5: 1.2.1,1.2.2 和WKWebView相同,多了一种方式!

唯一的区别就是在1.1.2上,同时Native调H5多了使用JSContext方式
WKWebView用的是:WKScriptMessageHandler
UIWebView用的是:JSContext
因为现在都用WKWebView了,具体使用参照老铁博客:https://www.jianshu.com/p/88345985fe94

你可能感兴趣的:(iOS Native和H5交互)