DSBridge 原生和H5交互看这里
之前用的WebViewJavascriptBridge和前端交互, 现在公司前端说统一用DSBridge, 研究了一下写了个demo, 分享给小伙纸们😃
三端易用的现代跨平台 Javascript bridge, 通过它,你可以在Javascript和原生之间同步或异步的调用彼此的函数.
https://github.com/wendux/DSBridge-IOS
特性
- Android、IOS、Javascript 三端易用,轻量且强大、安全且健壮。
- 同时支持同步调用和异步调用
- 支持以类的方式集中统一管理API
- 支持API命名空间
- 支持调试模式
- 支持API存在性检测
- 支持进度回调:一次调用,多次返回
- 支持Javascript关闭页面事件回调
- 支持Javascript 模态/非模态对话框
- Android端支持腾讯X5内核
安装
pod "dsBridge"
使用
- 新建一个类,实现API JS调用原生方法
//JS调用原生方法类
import Foundation
typealias JSCallback = (String, Bool)->Void
class JsApiTestSwift: NSObject {
var value = 10
var feedbankHandler : JSCallback?
var valueTimer: Timer?
// MARK: - 测试同步方法
//MUST use "_" to ignore the first argument name explicitly。
@objc func testSyn( _ arg:String) -> String {
print("js调用了原生的testSyn方法")
return String(format:"%@[Swift sync call:%@]", arg, "test")
}
// MARK: - 测试异步有回调
@objc func testAsyn( _ arg:String, handler: JSCallback) {
print("js调用了原生的testAsyn方法")
handler(String(format:"%@[Swift async call:%@]", arg, "test"), true)
}
// MARK: - 带有dic参数的
@objc func testNoArgSyn( _ args:Dictionary<String, Any>) -> String{
print("js调用了原生的testNoArgSyn方法")
return String("带有dic参数的的方法")
}
// MARK: - 持续返回进度
@objc func callProgress( _ args:Dictionary<String, Any> , handler: @escaping JSCallback ){
print("js调用了原生的callProgress方法")
feedbankHandler = handler
valueTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(feedbackValue), userInfo: nil, repeats: true)
}
//返回进度value
@objc func feedbackValue() {
if let handler = feedbankHandler {
if value > 0{
handler(String(value), false)//上传中
value -= 1
}else {
handler(String(value), true)//上传完成
}
}
}
}
可以看到,DSBridge正式通过API类的方式集中、统一地管理API。
- 新建一个UIViewController, 用来展示页面
import UIKit
class DSbridgeViewController: UIViewController {
private var webview:JMWebView = {
let webview:JMWebView = JMWebView.init()
return webview
}()
override func viewDidLoad() {
super.viewDidLoad()
//设置webview
setupWebview()
//JS调用原生
addJsMethod()
//原生调用JS
nativeCallJS()
}
// MARK: - 设置Webview
private func setupWebview() {
webview.frame = self.view.bounds
self.view.addSubview(webview)
#if DEBUG
webview.setDebugMode(true)
#endif
webview.customJavascriptDialogLabelTitles(["alertTitle" : "Notification", "alertBtn" : "OK"])
webview.navigationDelegate = self
let baseUrl = URL.init(fileURLWithPath: Bundle.main.bundlePath)
let htmlPath = Bundle.main.path(forResource: "test", ofType: "html") ?? ""
let htmlContent = (try? String.init(contentsOfFile: htmlPath, encoding: String.Encoding.utf8)) ?? ""
webview.loadHTMLString(htmlContent, baseURL: baseUrl)
}
// MARK: - JS调用原生
private func addJsMethod() {
//添加原生方法类
webview.addJavascriptObject(JsApiTestSwift.init(), namespace: nil)
webview.addJavascriptObject(JsApiTestSwift.init(), namespace: "swift")//增加命名空间, JS在调用的时候可以用 swift.methodName 方便管理功能模块可增强阅读
}
// MARK: - 原生调用JS
private func nativeCallJS() {
//原生调用js的addValue方法, 参数是[3, 4], 返回值是value
webview.callHandler("addValue", arguments: [3, 4]) { (value) in
print(value ?? "")
}
//拼接字符串
webview.callHandler("append", arguments: ["I", "love", "you"]) { (value) in
print(value ?? "")
}
//传递json
let dic: Dictionary = ["name": "weixiang", "sex": "male"]
let jsonStr: String = dicToString(dic) ?? ""
webview.callHandler("showJson", arguments: [jsonStr]) { (value) in
print(value ?? "")
}
//收到result 为json
let dic1: Dictionary = ["name": "zhangsan", "sex": "male"]
let jsonStr1: String = dicToString(dic1) ?? ""
webview.callHandler("showResult", arguments: [jsonStr1]) { (value) in
print(value ?? "")
}
webview.callHandler("startTimer") { (value) in
print(value ?? "")
}
//带有命名空间的方法
webview.callHandler("syn.addValue", arguments: [5, 6]) { (value) in
print(value as Any)
}
//测试是否js有这个方法
webview.hasJavascriptMethod("addValue") { (isHas) in
print(isHas)
}
//如果H5调用了window.close方法就会监听到
webview.setJavascriptCloseWindowListener {
print("监听到关闭H5页面")
}
}
}
添加API类实例到 DWKWebView
// MARK: - JS调用原生
private func addJsMethod() {
//添加原生方法类
webview.addJavascriptObject(JsApiTestSwift.init(), namespace: nil)
webview.addJavascriptObject(JsApiTestSwift.init(), namespace: "swift")//增加命名空间, JS在调用的时候可以用 swift.methodName 方便管理功能模块可增强阅读
}
调用Javascript API
```
// MARK: - 原生调用JS
private func nativeCallJS() {
//原生调用js的addValue方法, 参数是[3, 4], 返回值是value
webview.callHandler("addValue", arguments: [3, 4]) { (value) in
print(value ?? "")
}
//拼接字符串
webview.callHandler("append", arguments: ["I", "love", "you"]) { (value) in
print(value ?? "")
}
//传递json
let dic: Dictionary = ["name": "weixiang", "sex": "male"]
let jsonStr: String = dicToString(dic) ?? ""
webview.callHandler("showJson", arguments: [jsonStr]) { (value) in
print(value ?? "")
}
//收到result 为json
let dic1: Dictionary = ["name": "zhangsan", "sex": "male"]
let jsonStr1: String = dicToString(dic1) ?? ""
webview.callHandler("showResult", arguments: [jsonStr1]) { (value) in
print(value ?? "")
}
webview.callHandler("startTimer") { (value) in
print(value ?? "")
}
//带有命名空间的方法
webview.callHandler("syn.addValue", arguments: [5, 6]) { (value) in
print(value as Any)
}
//测试是否js有这个方法
webview.hasJavascriptMethod("addValue") { (isHas) in
print(isHas)
}
//如果H5调用了window.close方法就会监听到
webview.setJavascriptCloseWindowListener {
print("监听到关闭H5页面")
} }
命名空间
命名空间可以帮助你更好的管理API,这在API数量多的时候非常实用,比如在混合应用中。DSBridge (>= v3.0.0) 支持你通过命名空间将API分类管理,并且命名空间支持多级的,不同级之间只需用'.' 分隔即可。
调试模式
在调试模式时,发生一些错误时,将会以弹窗形式提示,并且原生API如果触发异常将不会被自动捕获,因为在调试阶段应该将问题暴露出来。如果调试模式关闭,错误将不会弹窗,并且会自动捕获API触发的异常,防止crash。强烈建议在开发阶段开启调试模式,可以通过如下代码开启调试模式:
webview.setDebugMode(true)
进度回调
通常情况下,调用一个方法结束后会返回一个结果,是一一对应的。但是有时会遇到一次调用需要多次返回的场景,比如在javascript钟调用端上的一个下载文件功能,端上在下载过程中会多次通知javascript进度, 然后javascript将进度信息展示在h5页面上,这是一个典型的一次调用,多次返回的场景,如果使用其它Javascript bridge, 你将会发现要实现这个功能会比较麻烦,而DSBridge本省支持进度回调,你可以非常简单方便的实现一次调用需要多次返回的场景,下面我们实现一个倒计时的例子:
// MARK: - 持续返回进度
@objc func callProgress( _ args:Dictionary<String, Any> , handler: @escaping JSCallback ){
print("js调用了原生的callProgress方法")
feedbankHandler = handler
valueTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(feedbackValue), userInfo: nil, repeats: true)
}
//返回进度value
@objc func feedbackValue() {
if let handler = feedbankHandler {
if value > 0{
handler(String(value), false)//上传中
value -= 1
}else {
handler(String(value), true)//上传完成
}
}
}
Javascript 弹出框
DSBridge已经实现了 Javascript的弹出框函数(alert/confirm/prompt),这些对话框按钮、标签文字默认都是中文的,如果你想自定义这些文本可以参考 customJavascriptDialogLabelTitles
API,如果你不想使用DSBridge实现的对话框,你可以通过设置DSUIDelegate
属性(是WKUIDelegate的代理属性)完全自定义。
另外注意,DSBridge实现的弹出框都是模态的,这会阻塞UI线程,如果你需要非模态的对话框,请参考disableJavascriptDialogBlock
API.
WKUIDelegate
在 DWKWebView
中,请使用DSUIDelegate
代替 UIDelegate
, 因为在DWKWebView
内部 UIDelegate
已经设置过了,而 DSUIDelegate
正是 UIDelegate
的一个代理。