3)Native 告诉 H5 分享结果
上面实现了 JS 传参数给 Native,但是 Native 还需要告诉 H5 分享结果。
📱 Native 实现代码如下
let userContent = WKUserContentController.init() userContent.add(self, name: "shareAction") let config = WKWebViewConfiguration.init() config.userContentController = userContent let wkWebView: WKWebView = WKWebView.init(frame: UIScreen.main.bounds, configuration: config) wkWebView.navigationDelegate = self wkWebView.uiDelegate = self view.addSubview(wkWebView) view.insertSubview(wkWebView, at: 0) wkWebView.load(URLRequest.init(url: URL.init(string: "http://192.168.2.1/js.html")!)) ... //代理方法,window.webkit.messageHandlers.xxx.postMessage(xxx)实现发送到这里 func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { print(message.body) print(message.name) print(message.frameInfo.request) if message.name == "shareAction" { let list = message.body as! [Any] let dict = list[0] as! [String: String] print(dict["title"]!) print(dict["content"]!) print(dict["url"]!) let shareSucc = list[1] as! String//获取回调JS,通知H5分享成功了 let script = "\(shareSucc)(true)" wkWebView?.evaluateJavaScript(script, completionHandler: nil) } }
获取shareSucc
的函数回调名称,在合适的时候我们可以通过这个JS函数回调,告诉H5我们的分享结果。
🖥 JS实现如下
<!DOCTYPE html> <html> <head> <title>js Bridge demo</title> <meta charset="utf-8"> <script type="text/javascript"> function shareSucc(isShare) { alert(isShare) } function btnClick() { try { window.webkit.messageHandlers.shareAction.postMessage([{ "title": "分享", "content": "内容", "url": "链接" }, "shareSucc"]) } catch (err) { alert(err) } } </script> </head> <body> <h1>js demo test</h1> <p style="text-align: center;"> <button type="button" onclick="btnClick()" style="font-size: 100px;">test JS</button> </p> </body> </html>
之前的postMessage
是发送的字典,由于我们的需求增多了,所以还是改成数组。
最后发送shareSucc
的字符串,告诉 Native 我们有一个shareSucc
的函数可以接收分享的结果。
JS 和 Native 统一封装
上面讲了 JS 回调 Native,Native 回调 JS,实现了我们常用的一些业务逻辑。
里面有很多重复的代码,实现起来也不友好,下面我们把这些重用的全部封装一下,改成好用的接口给上层,使 Native 和 JS 的开发人员都不用操心太多的实现细节。
🖥 H5界面的代码
<!DOCTYPE html> <html> <head> <title>js Bridge demo</title> <meta charset="utf-8"> <script type="text/javascript"> function shareSucc(isShare) { alert(isShare) } function reqUserInfoClick() { try { alert(iOSApp.getUserInfo()) } catch (err) { alert(err) } } function reqShareClick() { try { iOSApp.shareAction("分享title", "分享content", "分享url", "shareSucc") } catch (err) { alert(err) } } </script> </head> <body> <h1>js demo test</h1> <p style="text-align: center;"> <button type="button" onclick="reqUserInfoClick()" style="font-size: 100px;">获取用户信息</button> <button type="button" onclick="reqShareClick()" style="font-size: 100px;">执行分享</button> </p> </body> </html>
📦 封装 WKWebView 基类 JWebViewController
import UIKit import WebKit class JWebViewController: UIViewController, WKUIDelegate, WKScriptMessageHandler { private var mAsyncScriptArray:[JKWkWebViewHandler] = [] private var mSyncScriptArray:[JKWkWebViewHandler] = [] private var wkWebView: WKWebView? override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } public func startUrl(_ url: URL) { let configuretion = WKWebViewConfiguration() configuretion.preferences = WKPreferences() configuretion.preferences.javaScriptEnabled = true configuretion.userContentController = WKUserContentController() if self.mAsyncScriptArray.count != 0 || self.mSyncScriptArray.count != 0 { // 在载入时就添加JS // 只添加到mainFrame中 let script = WKUserScript(source: createScript(), injectionTime: .atDocumentStart, forMainFrameOnly: true) configuretion.userContentController.addUserScript(script) } //异步需要回调,所以需要添加handler for item in self.mAsyncScriptArray { configuretion.userContentController.add(self, name: item.name) } let wkWebView = WKWebView(frame: self.view.bounds, configuration: configuretion) wkWebView.uiDelegate = self self.view.insertSubview(wkWebView, at: 0) let request = URLRequest(url: url) wkWebView.load(request) self.wkWebView = wkWebView } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) //释放handler for item in self.mAsyncScriptArray { wkWebView?.configuration.userContentController.removeScriptMessageHandler(forName: item.name) wkWebView?.configuration.userContentController.removeAllUserScripts() } } // MARK: - 添加JS public func addAsyncJSFunc(functionName: String, parmers: [String], action: @escaping ([String:AnyObject]) -> Void) { var obj = self.mAsyncScriptArray.filter { (obj) -> Bool in return obj.name == functionName }.first if obj == nil { obj = JKWkWebViewHandler() obj!.name = functionName obj!.parmers = parmers obj!.action = action self.mAsyncScriptArray.append(obj!) } } public func addSyncJSFunc(functionName: String, parmers: [String]) { var obj = self.mSyncScriptArray.filter { (obj) -> Bool in return obj.name == functionName }.first if obj == nil { obj = JKWkWebViewHandler() obj!.name = functionName obj!.parmers = parmers self.mSyncScriptArray.append(obj!) } } // MARK: - 插入JS private func createScript() -> String { var result = "iOSApp = {" for item in self.mAsyncScriptArray { let pars = createParmes(dict: item.parmers) let str = "\"\(item.name!)\":function(\(pars)){window.webkit.messageHandlers.\(item.name!).postMessage([\(pars)]);}," result += str } for item in self.mSyncScriptArray { let pars = createParmes(dict: item.parmers) let str = "\"\(item.name!)\":function(){return JSON.stringify(\(pars));}," result += str } result = (result as NSString).substring(to: result.count - 1) result += "}" print("++++++++\(result)") return result } private func createParmes(dict: [String]) -> String { var result = "" for key in dict { result += key + "," } if result.count > 0 { result = (result as NSString).substring(to: result.count - 1) } return result } // MARK: - 执行JS public func actionJsFunc(functionName: String, pars: [AnyObject], completionHandler: ((Any?, Error?) -> Void)?) { var parString = "" for par in pars { parString += "\(par)," } if parString.count > 0 { parString = (parString as NSString).substring(to: parString.count - 1) } let function = "\(functionName)(\(parString));" wkWebView?.evaluateJavaScript(function, completionHandler: completionHandler) } // MARK: - WKUIDelegate public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { let alert = UIAlertController(title: "提示", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default, handler: { (_) -> Void in // We must call back js completionHandler() })) self.present(alert, animated: true, completion: nil) } // MARK: - WKScriptMessageHandler public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { let funcObjs = self.mAsyncScriptArray.filter { (obj) -> Bool in return obj.name == message.name } if let funcObj = funcObjs.first { let pars = message.body as! [AnyObject] var dict: [String: AnyObject] = [:] for i in 0..<funcObj.parmers.count { let key = funcObj.parmers[i] if pars.count > i { dict[key] = pars[i] } } funcObj.action?(dict) } } } class JKWkWebViewHandler: NSObject { fileprivate var name:String! fileprivate var parmers:[String]! fileprivate var action:(([String:AnyObject]) -> Void)? }