原生应用中或多或少都会接入H5界面,使用WKWebView加载,这就避免不了native与H5交互了。
传统交互方案:
直接系统系统API。 优点:简单 缺点:重复处理拦截事件 举例: 想在H5加载之前就传递给H5一些参数信息,可以这么干:
ini
// 初始化WKWebView时候,直接传递给H5一点参数。 这里以传给它平台信息为例。
let configuration = WKWebViewConfiguration()
configuration.applicationNameForUserAgent = "iOS"
let script = WKUserScript(source: "window.platform = 'iOS'", injectionTime: .atDocumentStart, forMainFrameOnly: true)
configuration.userContentController.addUserScript(script)
如果在加载界面后传递,使用evaluateJavaScript
方法即可:
csharp
// 这里也可以添加参数
let script = "window.webkit.messageHandlers.messageHandler.postMessage('Hello, H5!',{
resolution: "\#(resolution)",
chartType: \#(chartType)
})"
webView.evaluateJavaScript(script, completionHandler: nil)
添加交互事件: configuration.userContentController.add(self, name: "MessageHandle")
然后再代理WKScriptMessageHandler
方法里监听对应的事件即可:
swift
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
switch message.name {
case "MessageHandle":
print("处理H5数据")
default: break
}
}
以上是使用系统API,基础通信啦。但是这样,有多个事件,就要传多少次,很麻烦。 想这只监听一个方法,就能拦截所有的交互事件呢?已经有了现成轮子,Swift使用WKWebViewJavascriptBridge
。 WKWebViewJavascriptBridge
按照步骤,只需两步,就可完成接入。
1、确保Bridge初始化前已经有了webView!!! bridge = WKWebViewJavascriptBridge(webView: webView)
2、注册事件:
只需统一注册一个事件
JS_CALL_APP_FUN
来监听即可。
- // JS主动调用native的方法
- // 这是JS会调用JS_CALL_APP_FUN方法,这是native注册给JS调用的
- // JS需要回调,当然JS也可以传参数过来。data就是JS所传的参数,不一定需要传=
- // native端通过responseCallback回调JS端,JS就可以得到所需要的数据
less
jsBridge.register(handlerName: "JS_CALL_APP_FUN", handler: { parameters, responseCallBack in
// 解析处理JS调OC的事件,然后将结果callback返回给他
JSWebManager.appResponeJsCallBack(params: parameters, callBack: responseCallBack)
})
native 调用JS方法。 也只注册一个事件APP_CALL_JS_FUN
. 具体什么事件、以及参数,在data里传给JS.
css
jsBridge.callHandler:"APP_CALL_JS_FUN" data:@"传递给 JS 的参数") { responseData in
}
具体后续方法名啊、参数啊, 就与JS与约定好了。这样就能统一处理了。 不想写了,直接上我的部分代码自己看看吧。 请求传参:
ruby
{
'operate':'callPhone',
'data':{
'phone':'18888888888'//手机号码,必须
}
}
返回给JS数据:
csharp
{
'msg':'',//success或者为其他Native可能传给H5的错误提示信息
'code':0,//0 代表Native解析成功
'data':{
'token':'fewifhwiuhfuiewhf'
}
}
统一拦截处理方法:
先定义传递以及请求的Model:
less
/// 与H5交互,H5调用OC的方法传过来的Model
struct ExWebRequestModel<T: Codable>: Codable {
var operate: String = ""// 具体方法名称
var data: T
}
// data里单个参数的
struct ExWebRequestCommonModel: Codable {
var params: String?
var token: String?
...
}
struct ExWebRequestParamModel: Codable {
var params: ExWebParamsModel?
var path: String?
...
}
/// 如果有带额外参数,放在这里
struct ExWebParamsModel: Codable {
var isbn: String?
var studentId: String?
...
}
/// OC处理完成后返回给H5返回的Model
struct ExWebResponseModel: Codable {
var code: Int = 0
var msg: String = "sucess"
var data: ExWebResponDetailModel?
}
// 所有的返回数据都放在一个Model里,要哪个给哪个就行
struct ExamWebResponDetailModel: Codable {
var client: String?
var version: String?
var model: String? //手机型号
var appVersionName: String?
var appVersionCode: String? //版本号
var manufacturer: String? //手机厂商
var debug: Bool?
var name: String?
var userType: String?
var status: Int?
...
}
统一拦截处理Manager:
php
static func appResponeJsCallBack(params: [String: Any]?, callBack: ((_ responseData: Any?) -> Void)?) {
guard let paramsDict = params else { return }
guard let operate = paramsDict["operate"] else { return }
let operateType = JSCallAppFuncName(rawValue: operate as! String)
switch operateType {
// 这个优点特殊,单独处理
case .openPage:
do {
let jsonData = try JSONSerialization.data(withJSONObject: paramsDict, options: .prettyPrinted)
let paramsModel = try App.shared.decoder.decode(ExWebRequestModel<ExWebRequestParamModel>.self, from: jsonData)
// 处理具体的JS调OC事件
operateJsCallFunc(commonModel: ExWebRequestCommonModel(), paramsModel: paramsModel.data, operate: operateType ?? .none, callBack: callBack)
} catch {
print("Error: \(error)")
}
default:
do {
let jsonData = try JSONSerialization.data(withJSONObject: paramsDict, options: .prettyPrinted)
// 处理 data
// let jsonString = String(data: jsonData, encoding: .utf8)
let model = try App.shared.decoder.decode(ExWebRequestModel<ExWebRequestCommonModel>.self, from: jsonData)
print(model.operate)
// 处理具体的JS调OC事件
operateJsCallFunc(commonModel: model.data, paramsModel: ExamWebRequestParamModel(), operate: operateType ?? .none, callBack: callBack)
} catch {
print("Error: \(error)")
}
}
}
/// 解析JS掉用的哪个方法
static func operateJsCallFunc(commonModel: ExWebRequestCommonModel, paramsModel: ExWebRequestParamModel, operate: JSCallAppFuncName, callBack: ((_ responseData: Any?) -> Void)?) {
switch operate {
case .getClientInfo:
// 对应事件处理完成后,返回数据给JS
let model = ExWebResponseModel(data: ExamWebResponDetailModel(client: "iOS", version: Env.osVersion, model: UIDevice.current.type.rawValue, appVersionName: Env.appName, appVersionCode: Env.bundleShortVersion, manufacturer: "iPhone", debug: Env.isDebug))
jsCallFuncCallback(model: model, callBack: callBack)
case .openPage:
let paramsModel = paramsModel.path
// 拿到参数知道干啥事哈
// 回参数给JS
let model = ExamWebResponseModel()
jsCallFuncCallback(model: model, callBack: callBack)
case .getUserInfo:
let model = ExamWebResponseModel(data: ExamWebResponDetailModel(name: "小红", userType: "iOS客户端"))
jsCallFuncCallback(model: model, callBack: callBack)
...
default:
break
}
}
/// JS调用完成给它的回调信息- 具体哪个Model不同而已
static func jsCallFuncCallback(model: ExWebResponseModel, callBack: ((_ responseData: Any?) -> Void)?) {
do {
let jsonData = try App.shared.encoder.encode(model)
let jsonString = String(data: jsonData, encoding: .utf8)
callBack?(jsonString)
} catch {
print("Error: \(error)")
}
}
}
算了先这样吧,看不懂的再来。。