📄
Henry
  • Henry的神秘小屋
  • 其他
    • 字节码与机器码的区别?
  • ObjectiveC
    • NSInvocation
    • 检测OC项目中未使用的方法
    • Method Selector
    • 消息转发
  • Swift
    • 检测Swift项目中未使用的类 方法 属性
    • NSCoding
    • Mirror
    • JSONEncode JSONDecode
    • Any AnyObject AnyClass
    • is as? as! as
  • Cocoapods
    • 看懂Podfile.lock文件
    • 在编写的Pod中使用宏预编译
  • iOS
    • 苹果应用 保持画面流畅
    • 关于计算机展示图像的一些问题
    • @testable
    • iOS中URLRequest的缓存策略
    • CodePush接入流程
    • H5在WKWebView中读取沙盒文件
    • FDLog App客户端日志系统
    • 如何实现JSBridge基于WKWebView
    • 网络请求各个指标的度量
    • iOS13 UIModalPresentationStyle
    • 实现H5离线包机制
    • NSURLProtocol 拦截器
    • Framework
    • Lock
    • CFNetwork NSURLSession NSURLConnection
    • setNeedsLayout layoutIfNeeded layoutSubviews
    • StackView
    • Flutter Method Channel:从Dart到Native调用链
    • JSONSerialization.ReadingOptions
    • JSONSerialization.WritingOptions
    • RunLoop高级2
    • RunLoop高级1
    • RunLoop中级
    • RunLoop初级
    • LineBreak AutoShrink
    • 如何给H5出WebView调试包
    • TODO、FIXME、!!!、???、MARK
    • Operation的使用
    • UserDefault
    • 了解WKWebView
    • 输出日志信息到系统控制台
    • Float Double 失去精度问题
    • 使用xcodebuild命令打包导出
    • 在iOS项目中使用C++定义的模块
    • 证书问题
    • 创建常驻线程
  • 源码
    • 阅读PINCache源码
    • 解读AspectHook
    • HandyJSON是如何实现的?
  • 汇编
    • 看懂汇编
由 GitBook 提供支持
在本页
  • 前提掌握
  • 了解 DSBridge-IOS 原理
  • 总结

这有帮助吗?

  1. iOS

如何实现JSBridge基于WKWebView

上一页FDLog App客户端日志系统下一页网络请求各个指标的度量

最后更新于5年前

这有帮助吗?

当前环境: Xcode10.0 Swift4.2 iOS SDK 12.1

下面是原理实现代码,可以查看参考文章 + Demo 查看。 Demo:

前提掌握

背景概述: 随着 App 端 H5 的应用越来越多,H5 同学会遇到与 Native 同学一样的 Native 需求,那么这个时候就需要 Native 同学 与 H5 同学能进行 通信了,那么为基于 iOS 的 WKWebView 该如何找到正确通信方式呢?

  1. 了解 JavaScript ===[调用]===> Native

    这里我们只提 WKUIDelegate中的其中的一个方法,至于为什么选用这个回调方法,原因是因为 这个通道 可以让 Javascript 传递二个参数,并且还能得到一个同步的返回值。

    // Javascript 调用 prompt 方法 会触发 这个代理方法 
    // 例如: var result = prompt("AA", "BB")
    func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
        // 参数1 
        var param1 = prompt //"AA"
        // 参数2
        var param2 = defaultText // "BB"
        // 回调 result
        completionHandler("同步返回值 给Javascript")
    }
  2. 了解 Native ===[调用]===> JavaScript

    我们事先准备好 JS 方法 nativeMessageHandler 然后在代码中 找到一个时机, 调用即可 触发 Javascript方法。

    function nativeMessageHandler(method,param) {
        // 'action1','param1'
        console.log(method,param) 
    }
    // Native 代码
    
    // 注意: 此方法 只有在整个WebView都加载完成 才会有反应。
    webview.evaluateJavaScript("nativeMessageHandler('action1', 'param1')", completionHandler: { (feedback, error) in
    })

了解 DSBridge-IOS 原理

下面是纯干货了,至于为什么我们研究 DSBridge-IOS 这个库,是因为它能够满足我们开发的大部分场景,是基于 WKWebView 实现 JSBridge 的比较好的实现,那么我们就看看它的工作原理是什么,这样也有助于我们使用这种库。

下面是 DSBridge 与 Native 交互的两种方式: 一种是 JS 调用 Native 方法,另一种是 Native 调用 JS 方法

1. 同步调用 使用DSBridge第三方 如下:

// 同步 调用
function callSyn() {

 Void) {
}

然后这个协议回调方法里面,通过 JS 给 Native 的类名,方法名,通过 OC 的 Runtime 去动态的调用此方法,得到返回值,并且返回给回调函数 completionHandler 实现 Native 返回值给 JS 方法。 ">

    // 对应原生方法名: `testSyn`
    // 参数: `Hello` 
    // 类型: 同步调用
    dsBridge.call("testSyn", "Hello")
}
```
**实现原理**: 在`DSBridge`里面是通过[`WKUIDelegate`](https://zcoldwater.github.io/blog/article/ios/wkwebview/)去实现同步调用的,JS会通过调用`prompt`方法,唤起`Native`层的`WKUIDelegate`代理协议中的下面的方法 
```Swift
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
}
```
然后这个协议回调方法里面,通过`JS`给`Native`的类名,方法名,通过OC的Runtime去动态的调用此方法,得到返回值,并且返回给回调函数`completionHandler` 实现`Native`返回值给`JS`方法。

2. 异步调用 使用DSBridge第三方 如下:

// 异步 调用
function callAsyn() {

 Void) {
}

然后在拿到 WKUIDelegate 的回调后,Native 还会通过传来类名,方法名,根据 OC Runtime 去动态调用方法,然后拿到同步返回值,给 completionHandler 方法调用,返回给 JS,当通过 Runtime 动态调用到 Native 方法内的时候,我们触发回调参数,来回调给 JS,这步就需要方法 evaluateJavaScript 来实现,因为当初我们挂在整个 JS 下有一个回调函数,并且起了名,现在就需要回调给写调用 JS 这个全局的回调,并且把结果告知给它。 ">

    // 对应原生方法名: `testAsyn`
    // 参数: `hello`
    // 类型: 异步调用
    dsBridge.call("testAsyn","hello", function (value) {
        // 得到原生回调 参数。
        console.log(value)
    })
}
```
**实现原理**: 在`DSBridge`里面是通过[`WKUIDelegate`](https://zcoldwater.github.io/blog/article/ios/wkwebview/)和`evaluateJavaScript`方法来实现的`JS`调用`Native`然后`Native`回调给`JS`的。 
首先,`JS`方法调用`Native`附加回调参数,`JS`会把这个回调函数注册在`JS`全局下,比如说,挂在全局叫`BlockA`,然后继续通过调用`prompt`方法触发`WKUIDelegate`协议回调,方法在下面。
```Swift
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
}
```
然后在拿到`WKUIDelegate`的回调后,`Native`还会通过传来类名,方法名,根据OC Runtime去动态调用方法,然后拿到同步返回值,给`completionHandler`方法调用,返回给JS,当通过Runtime动态调用到`Native`方法内的时候,我们触发回调参数,来回调给`JS`,这步就需要方法`evaluateJavaScript`来实现,因为当初我们挂在整个`JS`下有一个回调函数,并且起了名,现在就需要回调给写调用`JS`这个全局的回调,并且把结果告知给它。

总结

上面我们分析了实现方式,无非都是用系统提供的与 JS 的通信能力,再来借助一下动态运行时来完成,虽然外表看起来神秘,但是经过源码阅读后,会发现其实还是很简单的。 以上全部是原作,如有建议和提议,不妨在下面留言,希望能对一些朋友有所帮助。

在这里我们参考 ,当然如果你已经看过这个库的源码,那么此篇文章可以跳过了。我这里会根据DSBridge 的源码介绍下它的JSBridge 的实践

如何实现 Javascript 调用 Native 呢? 其实 Apple 已经提供了这个通道 ,这个通道就是 WKUIDelegate,如果还不清楚可以

如何实现 Native 调用 Javascript 的方法呢,Apple 同样也给我们提供了通道, 这个通道就是 evaluateJavaScript 方法,如果你还不了解,如果还不清楚可以

我们只会针对其中的两种方法进行研究其原理 ,如果你没使用过可以去看下再回来可能会更好。

下面是整体流程图:

DSBridge-IOS
关于 WKWebView 一些必要的方法使用可以点击这里
点击这里查看
点击这里查看
DSBridge-IOS
https://github.com/zColdWater/wkwebviewdemo