iOS WKWebView的Hybrid流程

業(yè)務(wù)需求:藍(lán)牙外設(shè)<-->連接APP<-->打開WKWebView加載的Web小游戲目锭。
業(yè)務(wù)描述:希望藍(lán)牙設(shè)備設(shè)備通過連接APPWeb網(wǎng)頁進(jìn)行數(shù)據(jù)交互济蝉。Web中的游戲業(yè)務(wù)邏輯依賴藍(lán)牙設(shè)備傳輸?shù)臄?shù)據(jù)魏割,同時(shí)Web中由藍(lán)牙設(shè)備傳輸?shù)臄?shù)據(jù)觸發(fā)的某些業(yè)務(wù)狀態(tài)笆檀,回傳給藍(lán)牙設(shè)備刻伊。
所以喘漏,這里暫時(shí)先記錄下App和Web通信部分的邏輯做葵;藍(lán)牙部分后期補(bǔ)上占哟。


iOS(Native)部分

1.1、 iOS WKWebView的初始化部分

/// js 調(diào)用 原生 無參酿矢、無返回值的函數(shù)
private let jsCallNativeMethodName = "jsCallNativeMethod"
/// js 調(diào)用 原生 無參榨乎、有返回值的函數(shù)
private let jsCallNativeJsonStringMethodName = "jsCallNativeJsonStringMethod"
/// js 調(diào)用 原生 有參、無返回值或有返回值的函數(shù)
private let jsCallNativeJsonStringWithParamName = "jsCallNativeJsonStringWithParam"
/// 原生 調(diào)用 js 有參瘫筐、無返回值或有返回值的函數(shù)
private let nativeCallScriptName = "nativeCallScript"
var webView: WKWebView!
var requestUrl:URL?

/// 初始化WebView
func initWebView() {
    // 添加js對(duì)Native方法調(diào)用的監(jiān)聽
    let jsCalliOSMethons = [jsCallNativeMethodName,
                            jsCallNativeJsonStringMethodName,
                            jsCallNativeJsonStringWithParamName,
                            nativeCallScriptName]
    // native與JavaScript的交互管理
    let config = WKWebViewConfiguration()
    jsCalliOSMethons.forEach { name in
        config.userContentController.add(self, name: name)
    }
    
    webView = WKWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width,
height: UIScreen.main.bounds.size.height), configuration: config)
    webView.uiDelegate = self
    webView.navigationDelegate = self
    webView.allowsBackForwardNavigationGestures = true
    webView.scrollView.contentInsetAdjustmentBehavior = .never
    webView.scrollView.contentInset = .zero
    webView.scrollView.insetsLayoutMarginsFromSafeArea = false
    view.addSubview(webView)
    if (requestUrl != nil) {
        webView.load(URLRequest(url: requestUrl!))
    } else {
        let url:String = "http://192.168.1.4:7456/web-mobile/web-mobile/index.html"
        requestUrl = NSURL(string: url)! as URL
        webView.load(URLRequest(url: requestUrl!))
    }
}

override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // 移除注冊(cè)的js方法
        webView.configuration.userContentController.removeAllScriptMessageHandlers()
}

1.2谬哀、WKWebView的代理

WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate

//postMessage(message: any, targetOrigin: string, transfer?: Transferable[]): void;
//postMessage(message: any, options?: WindowPostMessageOptions): void;
func userContentController(_ userContentController: WKUserContentController, didReceive messa
WKScriptMessage) {
    // 通過接收J(rèn)S傳出消息的name,進(jìn)行捕捉的回調(diào)方法
    print("-- ??native 收到 js 調(diào)用:\(message.name) body:\(message.body)")
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    print("-- ??native did finish web load --")
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error)
    print("-- ??native did fail \(error.localizedDescription) --")
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!,
withError error: Error) {
    print("-- ??native did fail provisional navigation \(error.localizedDescription) --")
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    // 攔截到請(qǐng)求url
    print("-- ??native decide policy for \(String(describing: navigationAction.request.url)) 
")
    
    if navigationAction.request.url?.scheme?.caseInsensitiveCompare("jsCall") == .orderedSame
        decisionHandler(.cancel)
        return
    }
     
    decisionHandler(.allow)
}
//alert(message?: any): void;
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
    print("-- ??native alert message:\(message)")
    
    let alertViewController = UIAlertController(title: "JS Alert Msg", message: message,
preferredStyle: .alert)
    let sureAction = UIAlertAction(title: "Sure", style: .default) { action in
        completionHandler()
    }
    alertViewController.addAction(sureAction)
    self.present(alertViewController, animated: true)
}
// confirm(message?: string): boolean;
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
    print("-- ??native alert message:\(message)")
    
    let alertViewController = UIAlertController(title: "JS Confirm Msg", message: message,
preferredStyle: .alert)
    let sureAction = UIAlertAction(title: "Sure", style: .default) { action in
        completionHandler(true)
    }
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { action in
        completionHandler(false)
    }
    alertViewController.addAction(sureAction)
    alertViewController.addAction(cancelAction)
    self.present(alertViewController, animated: true)
}
// prompt(message?: string, _default?: string): string | null;
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String,
defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping
(String?) -> Void) {
    print("-- ??native run JS Text Input With Prompt:\(prompt)")
    
// let alertViewController = UIAlertController(title: "Text Input With Prompt", message: prompt, preferredStyle: .alert)
//
// alertViewController.addTextField { textField in
//   textField.text = defaultText
// }
//
// let sureAction = UIAlertAction(title: "Sure", style: .default) { action in
//   completionHandler(alertViewController.textFields?[0].text)
// }
// alertViewController.addAction(sureAction)
// self.present(alertViewController, animated: true)
    switch prompt {
    case jsCallNativeMethodName:// 無參、無返回值
        self.jsCallNativeMethod()
        completionHandler("")
        return
    case jsCallNativeJsonStringMethodName:// 無參严肪、有返回值
        completionHandler(self.jsCallNativeJsonStringMethod())
        return
    case jsCallNativeJsonStringWithParamName:
        completionHandler(self.jsCallNativeJsonStringWithParam(param: defaultText ?? ""))
        return
    default:
        // 不處理無效的prompt
        completionHandler("error param")
         return
    }
}

1.3、Native和JS的通信方法

extension GGWebViewController {
    //MARK: - 被js調(diào)用的iOS方法
    /// js 調(diào)用 原生 無參谦屑、無返回值的函數(shù)
    func jsCallNativeMethod() {
        print("-- ?? 這里是 原生 無參驳糯、無返回值的函數(shù) --")
        var paramToScript:String = "iOS 調(diào)用 js 方法傳遞的參數(shù)"
        self.dispatchEventToScript(param: paramToScript)
    }

    /// js 調(diào)用 原生 無參、有返回值的函數(shù)
    func jsCallNativeJsonStringMethod() -> String {
        let jsonString = "?? 這是 原生 無參氢橙、有返回值函數(shù) 返回給JS的值"
        return jsonString
    }
    
    /// 調(diào)用 原生 有參酝枢、無返回值或有返回值的函數(shù)
    /// 可通過param來判斷是否需要返回值
    func jsCallNativeJsonStringWithParam(param:String) -> String {
        guard let funcJsonData = param.data(using: .utf8) else{
            print("--- ??解析出錯(cuò)1 ---")
            return "param error"
        }
        do {
            guard let funcInfoDic = try JSONSerialization.jsonObject(with: funcJsonData) as? Dictionary<String, Any> else{
                print("---??解析出錯(cuò)3 --")
                return "json serialization error"
            }
            
            print("---??web回調(diào)需要返回值的函數(shù):\(funcInfoDic)")
            let jsonString = "?? Native接受JS參數(shù),并回調(diào)給JS"
            return jsonString
        } catch let errore {
            print("--- ??解析出錯(cuò)2 ---:\(errore.localizedDescription)")
            return errore.localizedDescription
        }
    }
    
    //MARK: - iOS調(diào)用js的方法
    func dispatchEventToScript(param: String) {
        var callBack:String = nativeCallScriptName + "(\"\(param)\")"
        webView.evaluateJavaScript(callBack) { response, error in
            if (error != nil) {
                print("-- ??\(self.nativeCallScriptName) error:\(String(describing: error))")
            } else {
                print("-- ??Native調(diào)用JS回調(diào)的參數(shù):\(String(describing: response))")
            }
        } 
    }
}

Web部分(TypeScript)

2.1悍手、Web訪問Native的方法(訪問的方法要在Native實(shí)現(xiàn)并注冊(cè))

    // -- JS 主動(dòng)調(diào)用 原生 方法的處理 (對(duì)象方法) --
     
    // 調(diào)用 原生 無參帘睦、無返回值的函數(shù)
    public requestMethod() {
        console.log("-- requestMethod --");
        window.prompt("jsCallNativeMethod"); 
    }
    // 調(diào)用 原生 無參袍患、有返回值的函數(shù)
    public requestMethodForValue() {
        let jsonString = window.prompt("jsCallNativeJsonStringMethod"); 
        console.log("-- requestMethodForValue" + jsonString + "--");
    }

    // 調(diào)用 原生 有參、無返回值的函數(shù)
    public requestMethodWithParam() {
        console.log("-- requestMethodWithParam --");
        window.prompt("jsCallNativeJsonStringWithParam","這條message是傳遞給Native的參數(shù)");
    }
    // 調(diào)用 原生 有參竣付、有返回值的函數(shù)
    public requestMethodWithParamForValue() {
        let jsonString = window.prompt("jsCallNativeJsonStringWithParam","{\"desc\":\"這條message是傳遞給Native的JSON字符串參數(shù)诡延,同時(shí)該函數(shù)可接受Native返回給js的值\"}");
        console.log("-- requestMethodWithParamForValue" + jsonString + "--");
    }

2.3、Native訪問Web的方法

/ -- 原生 主動(dòng)調(diào)用 JS 方法的處理 (??原生調(diào)用JS的方法一定要掛載到全局window下古胆,否則Native會(huì)調(diào)用JS方法失斔亮肌)--
// param:參數(shù) 可為空
// string:返回值 可為空
function nativeCallScript(param:string):string {

    console.log("-- nativeCallScript --");
    console.log(param);
 
    // 原生驅(qū)動(dòng)游戲角色運(yùn)動(dòng)
    if (param != null) {
        
    }
 
    let jsonstring = "這條Message是 Native 主動(dòng)調(diào)用 JS 方法,將Native傳遞過來的param處理后逸绎,回調(diào)給Native"+ "->param:(" + param + ")";
    return jsonstring;
}
(<any>window).nativeCallScript = nativeCallScript;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惹恃,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子棺牧,更是在濱河造成了極大的恐慌巫糙,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颊乘,死亡現(xiàn)場(chǎng)離奇詭異参淹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)疲牵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門承二,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纲爸,你說我怎么就攤上這事亥鸠。” “怎么了识啦?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵负蚊,是天一觀的道長。 經(jīng)常有香客問我颓哮,道長家妆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任冕茅,我火速辦了婚禮伤极,結(jié)果婚禮上姨伤,老公的妹妹穿的比我還像新娘乍楚。我一直安慰自己徒溪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布揍拆。 她就那樣靜靜地躺著芜壁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪顷牌。 梳的紋絲不亂的頭發(fā)上塞淹,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音谁帕,去河邊找鬼。 笑死康愤,一個(gè)胖子當(dāng)著我的面吹牛儡循,可吹牛的內(nèi)容都是我干的征冷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼今穿,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼腮出!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎械姻,沒想到半個(gè)月后楷拳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體她混,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年沉桌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谢鹊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡留凭,死狀恐怖佃扼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蔼夜,我是刑警寧澤兼耀,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響瘤运,放射性物質(zhì)發(fā)生泄漏窍霞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一拯坟、第九天 我趴在偏房一處隱蔽的房頂上張望但金。 院中可真熱鬧,春花似錦郁季、人聲如沸冷溃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽似枕。三九已至,卻和暖如春塞琼,著一層夾襖步出監(jiān)牢的瞬間菠净,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國打工彪杉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毅往,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓派近,卻偏偏與公主長得像攀唯,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子渴丸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容