self.wkWebView = [[WKWebView alloc] initWithFrame:frame configuration:[self _defaultConfiguration]];
wkwebview初始化時的參數配置html
wkwebview的存儲空間,通常是處理cookie,緩存等瀏覽器相關的臨時存儲前端
讀取cookie代碼java
[config.websiteDataStore fetchDataRecordsOfTypes:[NSSet<NSString *> setWithObject:WKWebsiteDataTypeCookies] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {}];
清理全部存儲(allWebsiteDataTypes)ios
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore]; [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) { for (WKWebsiteDataRecord *record in records) { [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{ NSLog(@"Cookies for %@ deleted successfully",record.displayName); }]; } }];
PS: 視頻播放器不全屏顯示 , iOS 10 如下使用 webkit-playsinline 屬性web
就是一個處理池,打開一個webview能夠指定從什麼池子裏打開,通常用默認或者指定一個單例WKProcessPool就好了objective-c
能夠指定userAgent中的application的名字,若是要修改整個UA,須要採用全局設置後端
是否自動播放視頻瀏覽器
if (@available(iOS 10.0, *)) { config.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; }else { config.mediaPlaybackRequiresUserAction = NO; }
WKPreferences的配置緩存
js注入,建立js句柄(bridge)等在後續js通訊處介紹安全
WKWebViewConfiguration另外的一些屬性配置
是否支持js,若是是no,html加載時候直接忽略js的加載
是否容許file路徑
[prefs setValue:@TRUE forKey:@"allowFileAccessFromFileURLs"];
wkwebview.UIDelegate屬性
用戶js中調用alert,confirm,prompt,若是不適配則沒法使用對應js功能,估計是安全問題,由於使用中有的會採用這個做爲bridge橋接
wkwebview.navigationDelegate屬性
監聽wkwebview整個生命週期的代理方法,詳細見"2、生命週期方法"
用戶點擊網頁上的連接,打開新頁面時,調用。
爲了兼容iOS8的js通訊,也能夠在這裏攔截url作bridge分發
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { BOOL isContinueRequest = YES; // NSString *jsNotId = [self __getJSNotificationId:[navigationAction.request URL]]; // NSString *urlStr = [navigationAction.request URL].absoluteString; // if (jsNotId) { // // 符合 js to native 的方法 // isRequest = NO; // [self handleJSBridgeGetJsonStringForJsNotId:jsNotId]; // }else if ([urlStr hasPrefix:@"ios://"]) { // // 特殊host攔截 // isRequest = NO; // [self handleSpecialJSBridgeTask:urlStr]; // } if ([self.ArleneWebViewDelegate respondsToSelector:@selector(ArleneWebView:shouldStartLoadWithRequest:navigationType:)]) { isContinueRequest = [self.ArleneWebViewDelegate ArleneWebView:webView shouldStartLoadWithRequest:navigationAction.request navigationType:UIWebViewNavigationTypeOther]; } if (isContinueRequest) {//容許 decisionHandler(WKNavigationActionPolicyAllow); } else {//不容許跳轉 decisionHandler(WKNavigationActionPolicyCancel); } }
正式發送請求前的回調,沒法攔截,能夠在這個點注入一些本身的js
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { ALLOG(@"webView->didStartProvisionalNavigation:"); [self importJS]; if([self.ArleneWebViewDelegate respondsToSelector:@selector(ArleneWebViewDidStartLoad:)]){ [self.ArleneWebViewDelegate ArleneWebViewDidStartLoad:webView]; } }
- (void) webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ ALLOG(@"webView->收到請求後 3 decidePolicyForNavigationResponse:"); if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response; NSInteger statusCode =response.statusCode; NSString * urlStr = response.URL.absoluteString; ALLOGF(@"當前狀態值:%ld;當前跳轉地址:%@",statusCode,urlStr); } //容許跳轉 decisionHandler(WKNavigationResponsePolicyAllow); //不容許跳轉 //decisionHandler(WKNavigationResponsePolicyCancel); }
回調該函數未必就表明了成功
回調該函數未必就表明了成功
回調該函數未必就表明了成功
若是訪問的頁面服務器出錯(返回500,400等非200的statusCode),這個方法也會被回調
//讀取成功 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { ALLOG(@"webView->didFinishNavigation:"); [self __ArleneWebViewDidFinishLoad]; if([self.ArleneWebViewDelegate respondsToSelector:@selector(ArleneWebViewDidFinishLoad:)]){ [self.ArleneWebViewDelegate ArleneWebViewDidFinishLoad:webView]; } if([self.ArleneWebViewDelegate respondsToSelector:@selector(ArleneWebViewAllFinishLoad:)]){ [self.ArleneWebViewDelegate ArleneWebViewAllFinishLoad:webView]; } }
2種請求錯誤:
好比:地址非法,DNS解析地址有問題,本地網絡問題
總之是尚未請求到服務器時候的錯誤,都會返回在這裏
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { ALLOG(@"webView->didFailProvisionalNavigation:"); [self __ArleneWebView:webView didFailLoadWithError:error]; }
打印的日誌
2020-06-04 14:09:33.416181+0800 ArleneiOS[7346:272402] -[ArleneWebView webView:decidePolicyForNavigationAction:decisionHandler:] [Line 551] webView->請求前 1 decidePolicyForNavigationAction:http://i.arlene.coms:3333/ 2020-06-04 14:09:33.423342+0800 ArleneiOS[7346:272402] webView->開始請求頁面 2 didStartProvisionalNavigation: 2020-06-04 14:09:37.021316+0800 ArleneiOS[7346:272402] webView->didFailProvisionalNavigation:
服務器接收到請求,並開始返回數據給到客戶端的過程當中出現傳輸錯誤
這個錯誤不是返回500,400等非200錯誤的回調
這個錯誤不是返回500,400等非200錯誤的回調
這個錯誤不是返回500,400等非200錯誤的回調
重要的事情說三遍
實際表現的錯誤多是你傳輸過程當中,斷網了或者服務器down掉了致使的錯誤
//地址正確,返回的response有問題 - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error { ALLOG(@"webView->didFailNavigation:"); [self __ArleneWebView:webView didFailLoadWithError:error]; }
打印的日誌
2020-06-04 14:06:10.950200+0800 ArleneiOS[7273:268811] -[ArleneWebView webView:decidePolicyForNavigationAction:decisionHandler:] [Line 551] webView->請求前 1 decidePolicyForNavigationAction:http://i.arlene.com:3333/ 2020-06-04 14:06:10.956527+0800 ArleneiOS[7273:268811] webView->開始請求頁面 2 didStartProvisionalNavigation: 2020-06-04 14:06:11.590449+0800 ArleneiOS[7273:268811] webView->收到請求後 3 decidePolicyForNavigationResponse: 2020-06-04 14:06:11.592887+0800 ArleneiOS[7273:268811] webView->內容開始返回 4 didCommitNavigation: 2020-06-04 14:06:48.776484+0800 ArleneiOS[7273:268811] webView->didFailNavigation:
對訪問網站的證書作驗證,並決定是否攔截
實際應用過程當中因爲涉及到第三方合做,因此基本採用所有放過+url白名單方式作控制
若是須要對證書作強校驗,能夠採用AFNetwork的認證證書方式作比對
// 若是須要證書驗證,與使用AFN進行HTTPS證書驗證是同樣的 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{ if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) { NSURLCredential *card = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust]; completionHandler(NSURLSessionAuthChallengeUseCredential,card); } }
不經常使用的說明以下
@protocol WKNavigationDelegate <NSObject> @optional // 主機地址被重定向時調用 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation; // 當內容開始返回時調用 - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation; //9.0才能使用,web內容處理中斷時會觸發 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0); @end
//1.建立request NSString* urlString = @"https://www.baidu.com"; NSURL* url=[NSURL URLWithString:urlString]; NSURLRequestCachePolicy cachePolicy = NSURLRequestUseProtocolCachePolicy; NSURLRequest *request =[NSURLRequest requestWithURL:url cachePolicy:cachePolicy timeoutInterval:0]; //2.請求 [self.wkWebView loadRequest:request];
若是須要自定義header,能夠採用NSMutableURLRequest,而後設置
[mutableRequest addValue:cid?:@"" forHTTPHeaderField:@"x-c-id"];
請求本地沙盒裏的頁面,主要是拼對URL就好了
注意url的頭部是「file:///」注意「斜槓」的數量是3個
或者直接使用
NSURL *fileURL = [NSURL fileURLWithPath:path]; NSURLRequest *request =[NSURLRequest requestWithURL:url cachePolicy:cachePolicy timeoutInterval:0];
而後發起請求
[self.wkWebView loadFileURL:request.URL allowingReadAccessToURL:[request.URL URLByDeletingLastPathComponent]]
PS:我發如今iOS13+模擬器上,直接用loadRequest也能夠訪問本地沙盒,並無權限問題,可是爲了減小兼容問題,仍是選擇使用本地讀取
內置包就是bundle包,就是將bundle包路徑拼接好,而後請求沙盒方式讀取頁面
自定義了一個url頭部"bundle://",在請求的時候作"file:///"頭部替換
直接把html文件讀出來之後,以頁面內容方式去讀取
[self.wkWebView loadHTMLString:htmlString baseURL:nil];
利用離線加載這一特性,咱們能夠經過服務端資源打包成本地資源包(zip包),經過服務器比對方式下載資源包,解壓後放在本地指定的沙盒目錄,隨後經過wkwebview加載本地方式打開頁面。
對於資源包要求
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy) { NSURLRequestUseProtocolCachePolicy = 0, // 默認策略,具體的緩存邏輯和協議的聲明有關,若是協議沒有聲明,不須要每次從新驗證cache。 NSURLRequestReloadIgnoringLocalCacheData = 1, // 忽略本地緩存,直接從後臺請求數據 NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // 忽略本地緩存數據、代理和其餘中介的緩存,直接從後臺請求數據 NSURLRequestReturnCacheDataElseLoad = 2, // // 優先從本地拿數據,且忽略請求生命時長和過時時間。可是若是沒有本地cache,則請求源數據 NSURLRequestReturnCacheDataDontLoad = 3, //只從本地拿數據 離線模式 NSURLRequestReloadRevalidatingCacheData = 5, // 從原始地址確認緩存數據的合法性後,緩存數據就可使用,不然從原始地址加載。 };
遵循web的緩存策略,簡單介紹:
分爲兩種緩存
1.對比緩存 (服務器方式比對,304)
須要和服務器作一次比對,可是不會拿回全部數據,因此請求快且輕。
Etag / If-None-Match :返回Etag給到客戶端,下次請求時header中將etag的值設置在If-None-Match 服務器作比對後客戶端比較後,決策是否緩存
Last-Modified / If-Modified-Since:原理相似上面,只不過是用時間的新舊來決策緩存
2.強緩存 (本地緩存,200 from memory cache/from disk cache)
Expires(1.0產物,基本能夠忽略) 第一次請求返回一個head,值是一個時間點,下次若是再請求相同資源,判斷時間是否過時,若是未過時則命中緩存
Cache-Control,主要指定max-age={xxx sencods}
忽略全部緩存,建議本地加載能夠採起這種方式,忽略緩存,由於緩存空間是有限的,不要影響真正須要緩存的頁面