美文网首页
WKWebView中Native与Web交互

WKWebView中Native与Web交互

作者: 奥利奥_2aff | 来源:发表于2019-12-25 13:20 被阅读0次

    数据交互的途径

    1. 拦截URL
    2. MessageHandler
    3. 注入JS代码
    4. evalueJavascript函数
    5. cookie
    6. 拦截http请求

    拦截URL

    实现关键: WKNavigationDelegate.

    通过实现WKNavigationDelegate的回调函数webView(_:decidePolicyFor:decisionHandler:)来拦截Web页面跳转的URL, Web端可以把数据放在URL中.

    MessageHandler

    实现关键: WKScriptMessageHandler, WKWebViewConfiguration.

    通过WKWebViewConfiguration, 向webView添加实现了WKScriptMessageHandler协议的对象, 监听来自JS端的调用.
    必须在WKWebView初始化的时候完成添加.

    let config = WKWebViewConfiguration()
    // someHandler是实现了WKScriptMessageHandler的实例化对象
    config.userContentController.add(someHandler, name: "nameOfSomeHandler")
    let web = WKWebView(frame: .zero, configuration: config)
    

    注入JS代码

    实现关键: WKUserScript, WKWebViewConfiguration.
    WKUserScript包装脚本代码, 通过WKWebViewConfiguration, 向webView添加可供Web端执行的代码.

    let config = WKWebViewConfiguration()
    // someScriptString是脚本代码字符串
    let someScriptString = "..."
    config.userContentController.addUserScript(WKUserScript(source: someScriptString, injectionTime: .atDocumentStart, forMainFrameOnly: true))
    let web = WKWebView(frame: .zero, configuration: config)
    

    evalueJavascript

    通过WKWebView提供的函数evalueJavascript(_:completionHandler:), 从Native端调用Web端的JS函数.

    /*
    @abstract Evaluates the given JavaScript string.
    @param javaScriptString The JavaScript string to evaluate.
    @param completionHandler A block to invoke when script evaluation completes or fails.
    @discussion The completionHandler is passed the result of the script evaluation or an error.
    */
    open func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)

    cookie

    iOS 中 cookie 有专门的类型HTTPCookie, iOS11 以前WKWebView的 cookie 和 app 共享, 在 iOS11 以后WKWebView的 cookie 会单独自己管理.
    获取cookie
    WKNavigationDelegate的回调webView(_:decidePolicyFor:decisionHandler:)参数中, 有一个WKNavigationResponse对象, 有几种方式可以从中取出cookie:

    1. HTTPCookie的静态函数cookies(withResponseHeaderFields:for:)
    2. HTTPCookieStorage(iOS 11以前和 WebKit 共享, iOS11以后WebKit 不包含在内)
    3. WKHTTPCookieStore(iOS 11以后)
    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
      if
        let response = navigationResponse.response as? HTTPURLResponse,
        let headers = response.allHeaderFields as? [String : String],
        let url = response.url
      {
          // 1.通过HTTPCookie获取
          HTTPCookie.cookies(withResponseHeaderFields: headers, for: url) // 
          // 2.通过HTTPCookieStorage获取
          HTTPCookieStorage.shared.cookies(for: url) // 获取对应此 URL 的 cookie
          HTTPCookieStorage.shared.cookies // 获取全部 cookie
      }
      // 3.通过WKHTTPCookieStore获取
      webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { (cookies) in
    
      }
      decisionHandler(.allow)
    }
    

    设置 cookie
    可以通过以下方式:

    1. HTTPCookieStorage(iOS 11以前)
    2. WKHTTPCookieStore(iOS 11以后)
    let url = URL(string: "https://www.jianshu.com")!
            
    if let cookie = HTTPCookie(properties: [HTTPCookiePropertyKey.commentURL: url]) {
         
        if #available(iOS 11.0, *) {
            // 2.WKHTTPCookieStore设置
            webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
        }else {
            // 1.HTTPCookieStorage设置
            HTTPCookieStorage.shared.setCookie(cookie)
            HTTPCookieStorage.shared.setCookies([cookie], for: url, mainDocumentURL: url) // 单独针对某 URL 设置
        }
    }
    

    补充: UIWebViewWKWebView 不一样, 它是通过HTTPCookieStorage和 APP 以及系统共享 cookie 的.

    拦截自定义http请求

    iOS11以后可以使用WKURLSchemeHandler协议拦截指定scheme的http请求.
    在iOS13以前可以通过私有API拦截scheme为http/https, iOS13后无法再访问私有变量, 因而无法实现此类请求的拦截.

    关键: WKWebViewConfiguration, WKURLSchemeHandler, WKURLSchemeTask.

    • 自定义一个实现了WKURLSchemeHandler协议的类型, 在回调中处理对应scheme的请求.
    class SomeURLSchemeHandler: NSObject, WKURLSchemeHandler {
        func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
            // 可以自己 直接创建 URLResponse 做回调, 或者另外调用网络请求获得 URLResponse
            ...
        }
        // 任务被告知要停止
        func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
            // 可以在此处把 webView(_:start:) 处发起的异步任务停掉
            ...
        }
    }
    
    • 自定义类需要在回调webView(_:start:)中操作WKURLSchemeTask对象, 根据情况调用对应的函数:
    /*
      如果任务已经被停止, 调用以下函数就会抛出异常
    */
    
    /*
      把自定义的响应传递过去, 必须调用至少1次.
      如果任务已经标记完成后再调用, 会抛出异常.
    */
    func didReceive(_ response: URLResponse)
    /*
      把响应数据传递进去, 必须在didReceive(_ response: URLResponse)之后调用
      如果任务已经标记完成后再调用, 会抛出异常.
    */
    func didReceive(_ data: Data)
    /*
      在响应成功后调用
      如果任务已经标记完成或者失败后再调用, 会抛出异常
    */
    func didFinish()
    /*
      在响应出错后调用
      如果任务已经标记完成或者失败后再调用, 会抛出异常
    */
    func didFailWithError(_ error: Error)
    
    • WKWebView初始化的时候, 把自定义请求拦截配置好.
    let config = WKWebViewConfiguration()
    config.setURLSchemeHandler(someURLSchemeHandler, forURLScheme: "someScheme")
    let webView = WKWebView(frame: .zero, configuration: config)
    

    小结

    • MessageHandler/JS 代码注入/请求拦截, 这3个方案都需要依赖WKWebViewConfiguration, 在 WebView 初始化的时候就要完成配置.
    • iOS13后, 无法通过私有API拦截 http/httpsscheme 的请求, 所以 hybrid 的时候很难做到对 web 端无入侵.
    • 为了做加载优化(资源, 数据), 可以使用自定义 scheme 的请求, 或者 web 端调用 MessageHandler 告知 native 端, 这需要两端协商加载的方案.
    • iOS11以后, WKWebView 的cookie不再是共享的, 需要单独针对维护.
    • 获取WKWebView的cookie可以从 WKNavigationResponse中获取.

    相关文章

      网友评论

          本文标题:WKWebView中Native与Web交互

          本文链接:https://www.haomeiwen.com/subject/tusznctx.html