1. 程式人生 > 其它 >智汀家庭雲-iOS端:業務功能【Web端專業版整合】

智汀家庭雲-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進行呼叫

//! 第一步:匯入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
    }