在iOS 8.0以后蘋果推出WKWebView诚撵,之前有性能問題的UIWebView基本就被棄用了捧韵,這里整理下我的WKWebView之旅和怎么封裝的丝蹭。
1互亮、WKWebView有個繞不過去的問題就是Cookie.
我們先來看下Cookie到底是個什么東西:
簡單地說犁享,cookie 就是瀏覽器儲存在用戶電腦上的一小段文本文件。cookie 是純文本格式豹休,不包含任何可執(zhí)行的代碼炊昆。一個 Web 頁面或服務(wù)器告知瀏覽器按照一定規(guī)范來儲存這些信息,并在隨后的請求中將這些信息發(fā)送至服務(wù)器,Web 服務(wù)器就可以使用這些信息來識別不同的用戶凤巨。大多數(shù)需要登錄的網(wǎng)站在用戶驗證成功之后都會設(shè)置一個 cookie视乐,只要這個 cookie 存在并可以,用戶就可以自由瀏覽這個網(wǎng)站的任意頁面敢茁。再次說明佑淀,cookie 只包含數(shù)據(jù),就其本身而言并不有害彰檬。緊跟 cookie 值后面的每個選項都以分號和空格分開伸刃,每個選擇都指定了 cookie 在什么情況下應(yīng)該被發(fā)送至服務(wù)器。
第一個選項是過期時間(expires)逢倍,指定了 cookie 何時不會再被發(fā)送至服務(wù)器捧颅,隨后瀏覽器將刪除該 cookie。該選項的值是一個 Wdy, DD-Mon-YYYY HH:MM:SS GMT 日期格式的值较雕。下一個選項是 domain碉哑,指定了 cookie 將要被發(fā)送至哪個或哪些域中。默認(rèn)情況下郎笆,domain會被設(shè)置為創(chuàng)建該 cookie 的頁面所在的域名谭梗,所以當(dāng)給相同域名發(fā)送請求時該 cookie 會被發(fā)送至服務(wù)器。另一個控制 Cookie 消息頭發(fā)送時機(jī)的選項是 path 選項宛蚓,和 domain 選項類似,path選項指定了請求的資源 URL 中必須存在指定的路徑時设塔,才會發(fā)送Cookie 消息頭凄吏。
這個比較通常是將 path 選項的值與請求的 URL 從頭開始逐字符比較完成的。如果字符匹配闰蛔,則發(fā)送 Cookie 消息頭痕钢。最后一個選項是 secure。不像其它選項序六,該選項只是一個標(biāo)記而沒有值任连。只有當(dāng)一個請求通過 SSL 或 HTTPS 創(chuàng)建時,包含 secure 選項的 cookie 才能被發(fā)送至服務(wù)器例诀。這種 cookie 的內(nèi)容具有很高的價值随抠,如果以純文本形式傳遞很有可能被篡改。
UIWebView Cookie
同一個應(yīng)用繁涂,不同UIWebView之間的Cookie是自動同步的拱她。并且可以被其他網(wǎng)絡(luò)類訪問比如NSURLConnection,AFNetworking。
它們都是保存在NSHTTPCookieStorage容器中扔罪。 當(dāng)UIWebView加載一個URL的時候秉沼,在加載完成時候,Http Response,對Cookie進(jìn)行寫入,更新或者刪除唬复,結(jié)果更新Cookie到NSHTTPCookieStorage存儲容器中矗积。
WKWebView Cookie
NSURLCache和NSHTTPCookieStroage無法操作(WKWebView)WebCore進(jìn)程的緩存和Cookie。
WKWebView實例將會忽略任何的默認(rèn)網(wǎng)絡(luò)存儲器(NSURLCache, NSHTTPCookieStorage, NSCredentialStorage) 和一些標(biāo)準(zhǔn)的自定義網(wǎng)絡(luò)請求類(NSURLProtocol,等等.)敞咧。
WKWebView實例不會把Cookie存入到App標(biāo)準(zhǔn)的的Cookie容器(NSHTTPCookieStorage)中,因為 NSURLSession/NSURLConnection等網(wǎng)絡(luò)請求使用NSHTTPCookieStorage進(jìn)行訪問Cookie,所以不能訪問WKWebView的Cookie棘捣,現(xiàn)象就是WKWebView存了Cookie,其他的網(wǎng)絡(luò)類如NSURLSession/NSURLConnection卻看不到妄均。柱锹,
與Cookie相同的情況就是WKWebView的緩存,憑據(jù)等丰包。WKWebView都擁有自己的私有存儲,因此和標(biāo)準(zhǔn)Cocoa網(wǎng)絡(luò)類兼容的不是那么好禁熏。
你也不能自定義requests(增加自己的http header,更改已經(jīng)存在的header)使用自定義的 URL schemes等等邑彪,因為NSURLProtocol也是不支持WKWebView的瞧毙。
WKWebView Cookie 寫入
1、JS注入:
WKUserContentController* userContentController = WKUserContentController.new;
WKUserScript * cookieScript = [[WKUserScript alloc]
initWithSource:[NSString stringWithFormat:@"document.cookie = '%@'", [self setCurrentCookie]]
injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:cookieScript];
- (NSString *)setCurrentCookie {
return @"";
}
2寄症、NSMutableURLRequest 注入
- (void)loadURLRequest {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url];
[request addValue:[self readCurrentCookie] forHTTPHeaderField:@"Cookie"];
[self.webView loadRequest:request];
}
- (NSString *)setCurrentCookie {
return @"";
}
劃重點:坑一:JS注入的Cookie宙彪,比如PHP代碼在Cookie容器中取是取不到的, javascript document.cookie能讀取到有巧,瀏覽器中也能看到释漆。
NSMutableURLRequest 注入的PHP等動態(tài)語言直接能從$_COOKIE對象中獲取到,但是js讀取不到篮迎,瀏覽器也看不到
所以合理的辦法讓js男图,php,瀏覽器都能讀取到相同的Cookie方法就是創(chuàng)建WebView的時候javascript注入Cookie甜橱,一開始發(fā)送NSMutableURLRequest請求的時候也要加上Cookie逊笆,并且保證兩個地方的設(shè)置的cookie一致。
坑二:WKWebView的cookie需要設(shè)置domain和path默認(rèn)情況下會帶進(jìn)去不是通用的岂傲。(今天剛發(fā)現(xiàn)的o)
坑三:網(wǎng)頁登錄跳原生之后登錄成功后dimis后你需要重新注入和刷新把cookie塞進(jìn)去难裆,所以你的viewWillAppear每次都需要重新加載WKWebView和重新loadRequest,簡直了镊掖。乃戈。。
KZWWebViewController 是如何做的呢堰乔?
我們知道偏化,app里經(jīng)常跳各種網(wǎng)頁,我們不可能每個網(wǎng)頁都去單獨處理镐侯,所以我們寫一個通用的可配置的KZWWebViewController侦讨,只需要傳url進(jìn)來就可以驶冒,其他你不要管了,是不是完美韵卤。
所以我們需要來設(shè)計一個這樣的KZWWebViewController骗污,首先必須的是跳轉(zhuǎn)的url和其他的配置參數(shù),然后是接入jsbridge沈条,方便我們和網(wǎng)頁的換下調(diào)用需忿,這樣我們的這個KZWWebViewController就基本滿足需求了。
所以我的做法是抽出一個類來管理蜡歹,它叫KZWRouterHelper屋厘,暴露一個方法
+ (void)pushbyPath:(NSString *)path xxx(xxx)xx .....
里面的操作是把你需要的配置的加上,然后轉(zhuǎn)成字典塞入router
NSDictionary *params = @{
@"path": [path kzw_urlEncode],
@"timestimp":[NSString stringWithFormat:@"%g", [[NSDate date] timeIntervalSince1970]]
};
NSString *url =
[NSString stringWithFormat:@"%@?%@", KZWWebViewControllerRouterPath, [NSURL elm_queryStringFromParameters:params]];
return [[ELMRouter sharedRouter] open:url animated:NO showStyle:ELMPageShowStylePush];
param里包含了你所以的配置月而,類如:
NSDictionary *params = @{
@"path": path,
@"fullScreen": @(NO),
@"fullUrl": @(NO),
@"title": string?string:@"",
@"timestimp":[NSString stringWithFormat:@"%g", [[NSDate date] timeIntervalSince1970]]
};
這個根據(jù)業(yè)務(wù)需求來配置就好汗洒,然后在controller里根據(jù)不同的參數(shù)做相應(yīng)的處理就可以了,這樣你的整個項目里所有的網(wǎng)頁跳轉(zhuǎn)就一行代碼就好了:
1
[KZWRouterHelper pushbyURL:@"xxxx" ];
接入的jsbirdge最好是選擇jscore的方式的父款,這樣是同步溢谤,網(wǎng)頁也可以加個配置方法,這個的主要目的憨攒,有的網(wǎng)頁需要由網(wǎng)頁自己來控制一些顯示世杀,原理同上我們自己的配置都是根據(jù)參數(shù)做不同的處理,具體看KZWWebViewController肝集,然后很多時候產(chǎn)品想要跳二級頁面的時候可以有2個返回瞻坝,這時候如果你的leftBarButtonItems是統(tǒng)配的話最好在頁面加載的時候就先設(shè)置一個返回,然后在didFinishNavigation代理設(shè)置成2個返回杏瞻,一個返回上一個網(wǎng)頁一個返回我們上一個控制器湿镀。這樣做主要是你開始設(shè)置成統(tǒng)配后來直接配2個會閃一下。
還有就是WKWebView中的進(jìn)度條伐憾,WKWebView的進(jìn)度條比較簡單你只要寫一個UIProgressView然后監(jiān)聽WKWebView的加載進(jìn)度就好了,然后title的顯示也是監(jiān)聽就好了赫模,如果沒取到記得設(shè)置一個默認(rèn)的树肃。
[self.webView addObserver:self
forKeyPath:NSStringFromSelector(@selector(estimatedProgress))
options:0
context:nil];
[self.webView addObserver:self forKeyPath:NSStringFromSelector(@selector(title)) options:NSKeyValueObservingOptionNew context:NULL];
然后是適配iPhone X的記得加這行代碼:
if (KZW_iPhoneX) {
self.webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
最后記得釋放你的監(jiān)聽:
- (NSString *)fullString:(NSString *)path {
NSString *domain = nil;
switch ([ELMEnvironmentManager environment]) {
case ELMEnvBeta:
domain = @"xxxxx";
break;
case ELMEnvAlpha:
domain = @"xxxxx";
break;
case ELMEnvProduction:
domain = @"xxxxx";
break;
default:
domain = @"xxxxx";
break;
}
if ([path containsString:@"http"]) {
return path;
}else {
return [domain stringByAppendingString:path];
}
}
- (void)dealloc {
self.webView.UIDelegate = nil;
[self.webView stopLoading];
[self.webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
[self.webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(title))];
self.webView = nil;
}
組url的時候可以加個環(huán)境的配置。
就完啦瀑罗,希望對你有用胸嘴。具體看KZWFoudation中的KZWWebViewController,KZWRouterHelper和KZWDSJavaScripInterface斩祭。https://github.com/ouyrp/KZWFoundation
哦還有一個cookie的清空和網(wǎng)頁清緩存我也加上吧劣像,前2篇關(guān)于WKWebView的文章就刪了
- (NSString *)readCurrentCookie {
return @"";
}
- (NSString *)setCurrentCookie {
return @"";
}
cookie清空只要這個2個方法里面參數(shù)清了就好了
if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore];
[dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]
completionHandler:^(NSArray<wkwebsitedatarecord *> * __nonnull records) {
for (WKWebsiteDataRecord *record in records)
{
if ( [record.displayName containsString:@"xxxxx"])
{
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes
forDataRecords:@[record]
completionHandler:^{
NSLog(@"Cookies for %@ deleted successfully",record.displayName);
}];
}
}
}];
}else {
NSString *librarypath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObject;
NSString *cookiesFolderPath = [librarypath stringByAppendingString:@"/Cookies"];
[[NSFileManager defaultManager] removeItemAtPath:cookiesFolderPath error:nil];
}
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
[cookieJar deleteCookie:cookie];
}</wkwebsitedatarecord *>
這是清緩存,親測有效
之后還是會出KZWFoudation的系列文章摧玫,寫下自己的封裝思路耳奕。