數(shù)據(jù)交互的途徑
- 攔截URL
MessageHandler
- 注入JS代碼
-
evalueJavascript
函數(shù) - cookie
- 攔截http請求
攔截URL
實現(xiàn)關鍵: WKNavigationDelegate
.
通過實現(xiàn)WKNavigationDelegate
的回調(diào)函數(shù)webView(_:decidePolicyFor:decisionHandler:)
來攔截Web頁面跳轉的URL, Web端可以把數(shù)據(jù)放在URL中.
MessageHandler
實現(xiàn)關鍵: WKScriptMessageHandler
, WKWebViewConfiguration
.
通過WKWebViewConfiguration
, 向webView添加實現(xiàn)了WKScriptMessageHandler
協(xié)議的對象, 監(jiān)聽來自JS端的調(diào)用.
必須在WKWebView
初始化的時候完成添加.
let config = WKWebViewConfiguration()
// someHandler是實現(xiàn)了WKScriptMessageHandler的實例化對象
config.userContentController.add(someHandler, name: "nameOfSomeHandler")
let web = WKWebView(frame: .zero, configuration: config)
注入JS代碼
實現(xiàn)關鍵: WKUserScript
, WKWebViewConfiguration
.
用WKUserScript
包裝腳本代碼, 通過WKWebViewConfiguration
, 向webView添加可供Web端執(zhí)行的代碼.
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
提供的函數(shù)evalueJavascript(_:completionHandler:)
, 從Native端調(diào)用Web端的JS函數(shù).
/*
@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
的回調(diào)webView(_:decidePolicyFor:decisionHandler:)
參數(shù)中, 有一個WKNavigationResponse
對象, 有幾種方式可以從中取出cookie:
-
HTTPCookie
的靜態(tài)函數(shù)cookies(withResponseHeaderFields:for:)
-
HTTPCookieStorage
(iOS 11以前和 WebKit 共享, iOS11以后WebKit 不包含在內(nèi)) -
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
可以通過以下方式:
-
HTTPCookieStorage
(iOS 11以前) -
WKHTTPCookieStore
(iOS 11以后)
let url = URL(string: "http://www.reibang.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 設置
}
}
補充: UIWebView
和 WKWebView
不一樣, 它是通過HTTPCookieStorage
和 APP 以及系統(tǒng)共享 cookie 的.
攔截自定義http請求
iOS11以后可以使用WKURLSchemeHandler
協(xié)議攔截指定scheme的http請求.
在iOS13以前可以通過私有API攔截scheme為http/https
, iOS13后無法再訪問私有變量, 因而無法實現(xiàn)此類請求的攔截.
關鍵: WKWebViewConfiguration
, WKURLSchemeHandler
, WKURLSchemeTask
.
- 自定義一個實現(xiàn)了
WKURLSchemeHandler
協(xié)議的類型, 在回調(diào)中處理對應scheme的請求.
class SomeURLSchemeHandler: NSObject, WKURLSchemeHandler {
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
// 可以自己 直接創(chuàng)建 URLResponse 做回調(diào), 或者另外調(diào)用網(wǎng)絡請求獲得 URLResponse
...
}
// 任務被告知要停止
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
// 可以在此處把 webView(_:start:) 處發(fā)起的異步任務停掉
...
}
}
- 自定義類需要在回調(diào)
webView(_:start:)
中操作WKURLSchemeTask
對象, 根據(jù)情況調(diào)用對應的函數(shù):
/*
如果任務已經(jīng)被停止, 調(diào)用以下函數(shù)就會拋出異常
*/
/*
把自定義的響應傳遞過去, 必須調(diào)用至少1次.
如果任務已經(jīng)標記完成后再調(diào)用, 會拋出異常.
*/
func didReceive(_ response: URLResponse)
/*
把響應數(shù)據(jù)傳遞進去, 必須在didReceive(_ response: URLResponse)之后調(diào)用
如果任務已經(jīng)標記完成后再調(diào)用, 會拋出異常.
*/
func didReceive(_ data: Data)
/*
在響應成功后調(diào)用
如果任務已經(jīng)標記完成或者失敗后再調(diào)用, 會拋出異常
*/
func didFinish()
/*
在響應出錯后調(diào)用
如果任務已經(jīng)標記完成或者失敗后再調(diào)用, 會拋出異常
*/
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/https
scheme 的請求, 所以 hybrid 的時候很難做到對 web 端無入侵. - 為了做加載優(yōu)化(資源, 數(shù)據(jù)), 可以使用自定義 scheme 的請求, 或者 web 端調(diào)用
MessageHandler
告知 native 端, 這需要兩端協(xié)商加載的方案. - iOS11以后,
WKWebView
的cookie不再是共享的, 需要單獨針對維護. - 獲取
WKWebView
的cookie可以從WKNavigationResponse
中獲取.