WKWebView中Native与Web交互

2019-12-25  本文已影响0人  奥利奥_2aff

数据交互的途径

  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.

class SomeURLSchemeHandler: NSObject, WKURLSchemeHandler {
    func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
        // 可以自己 直接创建 URLResponse 做回调, 或者另外调用网络请求获得 URLResponse
        ...
    }
    // 任务被告知要停止
    func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
        // 可以在此处把 webView(_:start:) 处发起的异步任务停掉
        ...
    }
}
/*
  如果任务已经被停止, 调用以下函数就会抛出异常
*/

/*
  把自定义的响应传递过去, 必须调用至少1次.
  如果任务已经标记完成后再调用, 会抛出异常.
*/
func didReceive(_ response: URLResponse)
/*
  把响应数据传递进去, 必须在didReceive(_ response: URLResponse)之后调用
  如果任务已经标记完成后再调用, 会抛出异常.
*/
func didReceive(_ data: Data)
/*
  在响应成功后调用
  如果任务已经标记完成或者失败后再调用, 会抛出异常
*/
func didFinish()
/*
  在响应出错后调用
  如果任务已经标记完成或者失败后再调用, 会抛出异常
*/
func didFailWithError(_ error: Error)
let config = WKWebViewConfiguration()
config.setURLSchemeHandler(someURLSchemeHandler, forURLScheme: "someScheme")
let webView = WKWebView(frame: .zero, configuration: config)

小结

上一篇下一篇

猜你喜欢

热点阅读