WKWebview与JSBridge日常使用记录

1,773 阅读4分钟

最近项目中因为涉及到WKWebview与JSBridge的交互有点多,目前采用的方案把已知问题都解决了。怕自己遗忘,所以记录下来,也供大家参考。

前情提要

应用是新闻类App,内部视图较为复杂且嵌入多方广告套件,多方考量之下,采取原生嵌套H5来实现,顶部nav部分由原生来做(类似于今日头条的顶部导航栏和频道栏),中间内容部分由H5来做,大方向定下来之后,最主要涉及的就是原生和H5的交互问题,JSBridge使用的是WebViewJavascriptBridge库,是一个简单易用的库,而且团队在之前的项目中也一直在使用,已经较为熟练。

问题记录

然而在使用过程中还是遇到了问题,问题记录如下:

  • 前端如果采取修改路由的方式,WKNavigationDelegate的didStartProvisionalNavigation没有回调。
  • 广告套件H5无法拿到跳转url,故不能通过JSBridge传给原生。
  • 广告套件种类过多,想要单纯靠WKNavigationDelegate的didStartProvisionalNavigation并不可靠。
  • H5那边有重定向,iOS这边本可以不用管,但某些网页重定向到了Http链接,Apple禁止了Http,大家应该都知道,只有添加白名单才可加载,不然会出现白屏。
  • 就算是Https也会存在不受信任或者过期的情况,不作处理也会出现白屏。

问题处理

  • 前端如果采取修改路由的方式,WKNavigationDelegate的didStartProvisionalNavigation没有回调。

这个可以JSBridge传给原生,原生实现新页面跳转,JSBridge的注册记得写在webview加载的后面,具体使用方法可以去WebViewJavascriptBridge库查看,简单来说,H5通知原生的场景,H5要调callHandler方法,原生要调registerHandler方法。反过来,原生通知H5的场景,H5要调registerHandler方法,原生要调callHandler方法。

  • 广告套件H5无法拿到跳转url,故不能通过JSBridge传给原生。

广告套件是外部链接,用WKNavigationDelegate的didStartProvisionalNavigation满足了大多数场景。

    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        //        startLoading()
        //处理广告套件的跳转
        if let url = webView.url, url != self.url, !url.absoluteString.contains(Settings.baseH5Url) {
            webView.stopLoading()
            self.openDetailVC(url: url, title: "新聞詳情")
        }
    }

  • 广告套件种类过多,想要单纯靠WKNavigationDelegate的didStartProvisionalNavigation并不可靠。

当didStartProvisionalNavigation不可靠时,可以尝试用另一个代理。

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        if let url = navigationAction.request.url, url != self.url, !url.absoluteString.contains(Settings.baseH5Url) {
            self.openDetailVC(url: url, title: "新聞詳情")
        }
        return nil
    }
  • H5那边有重定向,iOS这边本可以不用管,但某些网页重定向到了Http链接,Apple禁止了Http,大家应该都知道,只有添加白名单才可加载,不然会出现白屏。

Http链接未知,添加白名单就显得不现实,采取折中方案,弹框告知用户是Http链接,可以跳转Safari浏览器继续观看,重定向拦截也是webview的一个代理方法。

    func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
                //http增加弹框,防止http加载不了出现白屏
        if let webViewUrl = webView.url, webViewUrl.absoluteString.contains("http://") {
            showAlert(title: "http訪問", message: "http訪問不安全,是否允許打開瀏覽器訪問", buttonTitles: ["取消", "確認"], highlightedButtonIndex: 1) { index in
                if index == 1 {
                    if UIApplication.shared.canOpenURL(webViewUrl) {
                        if #available(iOS 10.0, *) {
                            UIApplication.shared.open(webViewUrl, options: [:], completionHandler: nil)
                        } else {
                            UIApplication.shared.openURL(webViewUrl)
                        }
                    }
                }
                self.navigationController?.popViewController(animated: true)
            }
        }
    }
  • 就算是Https也会存在不受信任或者过期的情况,不作处理也会出现白屏。

webview还有一个专门处理证书信任的代理方法,设为信任即可。

    func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
            let card = URLCredential.init(trust: challenge.protectionSpace.serverTrust!)
            completionHandler(.useCredential, card)
        }
    }

目前有用到的方法基本都列下了,webview还有一个代理是监听点击事件的,这个代理的返回过于频繁复杂,调试发现在页面加载的时候也会回调,不太好处理逻辑,但是知道一下留作备用。

optional func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)

小结

其实WKWebview我们团队已经在上线App中使用很久了,今年Apple就会要求开发者强制使用WKWebview了,对开发者而言,其实没多大差别,方法名都差不多,对我们团队而言最大的差别可能就是不能在storeboard中拖一个webview进去了,毕竟storeboard用惯了。感慨一下,我们能想到的问题,Apple基本都想到了。所以没有解决不了的场景,只有没有下苦功的我们。😂😂😂