生活随笔
收集整理的這篇文章主要介紹了
iOS之深入解析WKWebView的坑点收录和优化处理
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、Cookie 處理
① Cookie 說明
WKWebView 在設(shè)置 Cookie 的時(shí)候,經(jīng)常做的是在請(qǐng)求的請(qǐng)求頭里添加 Cookie,但這只是把 Cookie 發(fā)送給了服務(wù)端,本地并沒有保存 Cookie,Cookie 最終要寫到 WebView 的一個(gè) Cookie 文件目錄里面,后續(xù) WebView 里面自己的發(fā)起的請(qǐng)求或者跳轉(zhuǎn)才能在發(fā)起請(qǐng)求的時(shí)候,在對(duì)應(yīng)的域名下面取到 Cookie 傳出去。 Webview 加載 H5 頁面,實(shí)際上是把頁面相關(guān)的 .html、js、css 文件下載到本地,然后再加載,這時(shí)頁面去獲取 Cookie 的時(shí)候,是去本地 WebView 里的 Cookie 文件目錄里查找,如果沒有設(shè)置的話肯定就獲取不到,所以在設(shè)置 Cookie 的時(shí)候,服務(wù)端和客戶端都要設(shè)置。
② 服務(wù)端 Cookie 設(shè)置
在使用 UIWebView 的時(shí)候,是通過 NSHTTPCookieStorage 來管理 Cookie 的,如下,給 baidu.tech 這個(gè)域名添加一個(gè)名為 user 的 Cookie:
var props
= Dictionary
< HTTPCookiePropertyKey
, Any
> ( ) props
[ HTTPCookiePropertyKey
. name
] = "user" props
[ HTTPCookiePropertyKey
. value
] = "admin" props
[ HTTPCookiePropertyKey
. path
] = "/" props
[ HTTPCookiePropertyKey
. domain
] = "baidu.tech" props
[ HTTPCookiePropertyKey
. version
] = "0" props
[ HTTPCookiePropertyKey
. originURL
] = "baidu.tech" if let cookie
= HTTPCookie ( properties
: props
) { HTTPCookieStorage
. shared
. setCookie ( cookie
) }
WKWebView Cookie 問題在于 WKWebView 發(fā)起的請(qǐng)求不會(huì)自動(dòng)帶上存儲(chǔ)于 NSHTTPCookieStorage 容器中的 Cookie。解決辦法也很簡單,就是在 WKWebView 發(fā)起請(qǐng)求之前,先從 NSHTTPCookieStorage 讀取 Cookie,然后手動(dòng)往 URLRequest 的請(qǐng)求頭里添加一下 Cookie:
func
getCookie ( ) -> String
{ var cookieString
= "" if let cookies
= HTTPCookieStorage
. shared
. cookies
{ for cookie
in cookies
{ if cookie
. domain
== cookieDomain
{ let str
= "\(cookie.name)=\(cookie.value)" cookieString
. append ( "\(str);" ) } } return cookieString
} var request
= URLRequest ( url
: URL ( string
: "https://baidu.tech" ) ) request
. addValue ( getCookie ( ) , forHTTPHeaderField
: "Cookie" )
當(dāng)服務(wù)器頁面發(fā)生重定向的時(shí)候,此時(shí)第一次在 RequestHeader 中寫入的 Cookie 會(huì)丟失,還需要對(duì)重定向的請(qǐng)求重新做添加 Cookie 的處理。
② 客戶端 Cookie 設(shè)置
當(dāng)頁面加載的時(shí)候,后端無論是啥語言,都能從請(qǐng)求頭里看到 Cookie 了,但是后端渲染返回頁面后,在客戶端的 WebView 里運(yùn)行的時(shí)候,JS 在執(zhí)行的時(shí)候調(diào)用 document.cookie API 是讀取不到 Cookie 的,所以還得針對(duì)客戶端 Cookie 進(jìn)行處理:
var cookieString
= "" if let cookies
= HTTPCookieStorage
. shared
. cookies
{ for cookie
in cookies
{ if cookie
. domain
== "baidu.tech" { let str
= "\(cookie.name)=\(cookie.value)" cookieString
. append ( "document.cookie='\(str);path=/;domain=baidu.tech';" ) } } } let cookieScript
= WKUserScript ( source
: cookieString
, injectionTime
: . atDocumentStart
, forMainFrameOnly
: false
) let userContentController
= WKUserContentController ( ) userContentController
. addUserScript ( cookieScript
) let webViewConfig
= WKWebViewConfiguration ( ) webViewConfig
. userContentController
= userContentControllerlet webV
= WKWebView ( frame
: CGRect
. zero
, configuration
: webViewConfig
)
客戶端 Cookie 注入實(shí)際上就是創(chuàng)建一個(gè) JS 腳本,讓 WebView 去執(zhí)行,推薦在 .atDocumentStart 這個(gè)時(shí)機(jī)進(jìn)行預(yù)置靜態(tài) JS 的注入,這樣 WebView 在加載后端返回的靜態(tài)頁面的時(shí)候,就可以拿到保存著客戶端的 Cookie 了。
二、URL 攔截
① Web 頁面重定向問題
在 WKWebView 中,每一次頁面跳轉(zhuǎn)之前,都會(huì)調(diào)用下面的回調(diào)函數(shù):
func
webView ( _ webView
: WKWebView
, decidePolicyFor navigationAction
: WKNavigationAction
, decisionHandler
: @ escaping ( WKNavigationActionPolicy
) -> Void
)
重定向問題有兩種: 服務(wù)器頁面重定向,需要對(duì)新發(fā)起的請(qǐng)求重新設(shè)置 Cookie; 本地頁面重定向,只要客戶端設(shè)置了 Cookie,那么就不需要再處理。 因此,如果是服務(wù)器頁面重定向,那么判斷此時(shí) Request 是否有需要的 Cookie,沒有就 Cancel 掉,修改 Request 重新發(fā)起。
func
webView ( _ webView
: WKWebView
, decidePolicyFor navigationAction
: WKNavigationAction
, decisionHandler
: @ escaping ( WKNavigationActionPolicy
) -> Void
) { var shouldCancelLoadURL
= false
if let cookie
= navigationAction
. request
. value ( forHTTPHeaderField
: "Cookie" ) { if cookie
. contains ( "user" ) { shouldCancelLoadURL
= false
} else { var request
= URLRequest ( url
: URL ( string
: ( navigationAction
. request
. url
? . absoluteString
) ! ) ! ) request
. addValue ( getCookie ( ) , forHTTPHeaderField
: "Cookie" ) webView
. load ( request
) shouldCancelLoadURL
= true
} } else { var request
= URLRequest ( url
: URL ( string
: ( navigationAction
. request
. url
? . absoluteString
) ! ) ! ) request
. addValue ( getCookie ( ) , forHTTPHeaderField
: "Cookie" ) webView
. load ( request
) shouldCancelLoadURL
= true
} if shouldCancelLoadURL
{ decisionHandler ( WKNavigationActionPolicy
. cancel
) } else { decisionHandler ( WKNavigationActionPolicy
. allow
) } }
② 跨域問題
針對(duì)跨域的問題,解決辦法和上面的方法類似,僅僅是判斷條件不同:
func
webView ( _ webView
: WKWebView
, decidePolicyFor navigationAction
: WKNavigationAction
, decisionHandler
: @ escaping ( WKNavigationActionPolicy
) -> Void
) { var shouldCancelLoadURL
= false
if let url
= navigationAction
. request
. url
? . absoluteString
{ if url
. contains ( "baidu.tech" ) { shouldCancelLoadURL
= false
} else { shouldCancelLoadURL
= true
} } else { shouldCancelLoadURL
= true
} if shouldCancelLoadURL
{ decisionHandler ( WKNavigationActionPolicy
. cancel
) } else { decisionHandler ( WKNavigationActionPolicy
. allow
) } }
③ 非法跳轉(zhuǎn)請(qǐng)求攔截
非法跳轉(zhuǎn)請(qǐng)求攔截就是由網(wǎng)頁發(fā)出一條新的跳轉(zhuǎn)請(qǐng)求,跳轉(zhuǎn)的目的地是一個(gè)非法的壓根就不存在的地址,比如:
https
: dwdwdw
:
url 地址組成如下: 協(xié)議:也就是 http/https/file 等,非法請(qǐng)求通信地址用了 dwdwdw; 域名:上面的 .baidu.com 或 yangdw; 參數(shù):上面的 xx=xx 或 param=paramobj; 如果構(gòu)建一條假 url: 用協(xié)議與域名當(dāng)做通信識(shí)別; 用參數(shù)當(dāng)做數(shù)據(jù)傳遞; 客戶端會(huì)無差別攔截所有請(qǐng)求,真正的 url 地址應(yīng)該照常放過,只有協(xié)議域名匹配的 url 地址才應(yīng)該被客戶端攔截,攔截下來的 url 不會(huì)導(dǎo)致 WebView 繼續(xù)跳轉(zhuǎn)錯(cuò)誤地址,因此無感知,相反攔截下來的 url 可以讀取其中路徑當(dāng)做指令,讀取其中參數(shù)當(dāng)做數(shù)據(jù),從而根據(jù)約定調(diào)用對(duì)應(yīng)的 Native 原生代碼。 以上其實(shí)是一種協(xié)議約定,只要 JS 按著這個(gè)約定協(xié)議生成假 url,Native 按著約定協(xié)議攔截/讀取假 url,整個(gè)流程就能跑通。
三、User-Agent 設(shè)置
① 全局設(shè)置
App 內(nèi)所有 Web 請(qǐng)求的 User-Agent 全部被修改:
let webView
= UIWebView ( frame
: CGRect
. zero
) let userAgent
= webView
. stringByEvaluatingJavaScript ( from
: "navigator.userAgent" ) if let agent
= userAgent
{ let user
= "@\(agent);extra_user_agent" let dict
= [ "UserAgent" : user
] UserDefaults
. standard
. register ( defaults
: dict
) } let webV
= WKWebView ( frame
: CGRect
. zero
) webV
. evaluateJavaScript ( "navigator.userAgent" ) { ( result
, error
) in if let oldAgent
= result as
? String
{ let user
= "@\(oldAgent);extra_user_agent" let dict
= [ "UserAgent" : user
] UserDefaults
. standard
. register ( defaults
: dict
) } }
② 單個(gè) WebView 設(shè)置
在 iOS9,WKWebView 提供了一個(gè)非常便捷的屬性去更改 User-Agent,就是 customUserAgent 屬性,這樣使用起來不僅方便,也不會(huì)全局更改 User-Agent,可惜的是 iOS9 才有,如果適配 iOS8,還是需要使用上面的方法。
let webView
= UIWebView ( frame
: CGRect
. zero
) let userAgent
= webView
. stringByEvaluatingJavaScript ( from
: "navigator.userAgent" ) if let agent
= userAgent
{ let user
= "@\(agent);extra_user_agent" webView
. customUserAgent
= user
}
總結(jié)
以上是生活随笔 為你收集整理的iOS之深入解析WKWebView的坑点收录和优化处理 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。