智汀家庭雲-iOS端:業務功能【Web端專業版整合】
智汀家庭雲iOS版,使用WKWebView的WKScriptMessageHandler實現Web端專業版整合。
WKWebView是Apple在iOS8推出的WebKit框架中的負責網頁的渲染與展示的類,相比UIWebView速度更快,佔用記憶體更少,支援更多的HTML特性。WKScriptMessageHandler是WebKit提供的一種在WKWebView上進行JS訊息控制的協議。
一、iOS呼叫JS:
iOS呼叫JS方式是通過WKWebView的-evaluateJavaScript:completionHandler的方法來實現的。
檔案:WKHandlerSwift.swift
/// 執行js指令碼
/// - Parameters:
/// - js: js
/// - completed: completed
/// - Returns: void
public func evaluateJavaScript(js:String!, withCompleted completed:((_ data:Any?, _ error:Error?) ->Void)?) -> Void {
self.webView.evaluateJavaScript(js, completionHandler: { (data:Any?, error:Error?) in
completed?(data,error)
})
}
/// 執行js指令碼,同步返回
/// - Parameters:
/// - js: js
/// - error: error
/// - Returns: void
public func synEvaluateJavaScript(js:String!, withError error:inout UnsafeMutablePointer<Error>?) -> Any? {
var result:Any?
var success:Bool? = false
var result_Error:Error?
self.evaluateJavaScript(js: js, withCompleted: { (data:Any?, tmp_error:Error?) in
if tmp_error != nil {
result = data
success = true
} else {
result_Error = tmp_error
}
})
while success != nil {
RunLoop.current.run(mode: .default, before: .distantFuture)
}
if error != nil {
do {
try error = withUnsafeMutablePointer(to: &result_Error, result_Error as! (UnsafeMutablePointer<Error?>) throws -> UnsafeMutablePointer<Error>?)
} catch {
#if DEBUG
print("WKEventHandlerNameSwift error:%@",error)
#endif
}
}
return result
}
二、JS呼叫iOS
WKEventHandlerProtocol代理方法:JS與iOS定義好方法名稱,方便JS進行呼叫
-
JS程式碼:Web-擴充套件開發:jsbridge使用
-
iOS程式碼:
//! 第一步:匯入WebKit框架標頭檔案
import WebKit
//! 第二步:WKWebViewWKScriptMessageHandlerController遵守WKScriptMessageHandler協議
extension DeviceWebViewController: WKEventHandlerProtocol{
}
//! 第三步:使用添加了ScriptMessageHandler的userContentController配置configuration
eventHandler = WKEventHandlerSwift(webView, self)
let config = WKWebViewConfiguration()
config.preferences = WKPreferences()
config.preferences.minimumFontSize = 10
config.preferences.javaScriptEnabled = true
config.preferences.javaScriptCanOpenWindowsAutomatically = true
config.processPool = WKProcessPool()
config.applicationNameForUserAgent = "zhitingua " + (config.applicationNameForUserAgent ?? "")
//! 第四步:為userContentController新增ScriptMessageHandler,並指明name
let usrScript:WKUserScript = WKUserScript.init(source: WKEventHandlerSwift.handleJS(), injectionTime: .atDocumentStart, forMainFrameOnly: true)
config.userContentController = WKUserContentController()
config.userContentController.addUserScript(usrScript)
config.userContentController.add(self.eventHandler, name: WKEventHandlerNameSwift)
//! 第五步:使用configuration物件初始化webView
webView = WKWebView(frame: .zero, configuration: config)
webView.uiDelegate = self
webView.navigationDelegate = self
webView.allowsBackForwardNavigationGestures = true
webView.scrollView.contentInsetAdjustmentBehavior = .never
webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
//! 第六步:WKWebView收到ScriptMessage時回撥此方法
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name caseInsensitiveCompare:@"jsToOc"] == NSOrderedSame) {
[WKWebViewWKScriptMessageHandlerController showAlertWithTitle:message.name message:message.body cancelHandler:nil];
}
}
extension DeviceWebViewController: WKEventHandlerProtocol {
//MARK:WKEventHandlerProtocol
func nativeHandle(funcName: inout String!, params: Dictionary<String, Any>?, callback: ((Any?) -> Void)?) {
if funcName == "networkType" {
networkType(callBack: callback)
} else if funcName == "setTitle" {
setTitle(params: params ?? [:])
} else if funcName == "getUserInfo" {
getUserInfo(callBack: callback)
} else if funcName == "isApp" {
isApp(callBack: callback)
} else if funcName == "isProfession" {
isProfession(callBack: callback)
}
}
//下面為執行的方法
func setTitle(params:Dictionary<String,Any>) {
}
func networkType(callBack:((_ response:Any?) -> ())?) {
}
...
}
-
實現原理:
1、JS與iOS約定好jsToOc方法,用作JS在呼叫iOS時的方法;
2、iOS使用WKUserContentController的addScriptMessageHandler:name方法監聽name為jsToOc的訊息;
3、JS通過window.webkit.messageHandlers.func.postMessage()的方式對js呼叫iOS方法傳送訊息;
4、iOS在userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)方法中讀取name為 “zhitingua” 的訊息資料message.body。
-
注意事項:
config.userContentController.add(self.eventHandler, name: “”)會引起迴圈引用問題。
一般來說,在合適的時機removeScriptMessageHandler可以解決此問題,如下:
/// 清空handler的資料資訊, 注入的指令碼。繫結事件資訊等等
/// - Parameter handler: handler
/// - Returns: void
public func cleanHandler( handler:inout WKEventHandlerSwift!) -> Void {
if (handler.webView != nil) {
handler.webView.evaluateJavaScript("zhiting.removeAllCallBacks();", completionHandler: nil)
handler.webView.configuration.userContentController.removeScriptMessageHandler(forName: WKEventHandlerNameSwift)
}
handler = nil
}