iOS WKWebView適配(基礎篇)

1、初始化

1.initWithFrame:configuration

self.wkWebView = [[WKWebView alloc] initWithFrame:frame configuration:[self _defaultConfiguration]];

2.WKWebViewConfiguration類說明

wkwebview初始化時的參數配置html

websiteDataStore

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);
                }];
            }
        }];

allowsInlineMediaPlayback

PS: 視頻播放器不全屏顯示 , iOS 10 如下使用 webkit-playsinline 屬性web

processPool

就是一個處理池,打開一個webview能夠指定從什麼池子裏打開,通常用默認或者指定一個單例WKProcessPool就好了objective-c

applicationNameForUserAgent

能夠指定userAgent中的application的名字,若是要修改整個UA,須要採用全局設置後端

mediaTypesRequiringUserActionForPlayback(iOS10+)/mediaPlaybackRequiresUserAction(iOS10-)

是否自動播放視頻瀏覽器

if (@available(iOS 10.0, *)) {
    config.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
}else {
    config.mediaPlaybackRequiresUserAction = NO;
}

preferences

WKPreferences的配置緩存

其它參數

js注入,建立js句柄(bridge)等在後續js通訊處介紹安全

3.WKPreferences類說明

WKWebViewConfiguration另外的一些屬性配置

javaScriptEnabled

是否支持js,若是是no,html加載時候直接忽略js的加載

KVC設置 allowFileAccessFromFileURLs

是否容許file路徑

[prefs setValue:@TRUE forKey:@"allowFileAccessFromFileURLs"];

4.WKUIDelegate

wkwebview.UIDelegate屬性

用戶js中調用alert,confirm,prompt,若是不適配則沒法使用對應js功能,估計是安全問題,由於使用中有的會採用這個做爲bridge橋接

5.WKNavigationDelegate

wkwebview.navigationDelegate屬性

監聽wkwebview整個生命週期的代理方法,詳細見"2、生命週期方法"

2、生命週期方法(WKNavigationDelegate)

1.請求前決定是否要跳轉

用戶點擊網頁上的連接,打開新頁面時,調用。

爲了兼容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);
    }
}

2.頁面開始請求

正式發送請求前的回調,沒法攔截,能夠在這個點注入一些本身的js

- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    ALLOG(@"webView->didStartProvisionalNavigation:");
    [self importJS];
    if([self.ArleneWebViewDelegate respondsToSelector:@selector(ArleneWebViewDidStartLoad:)]){
        [self.ArleneWebViewDelegate ArleneWebViewDidStartLoad:webView];
    }
}

3.收到響應後決定是否跳轉

- (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);
}

4-1.加載完成

回調該函數未必就表明了成功

回調該函數未必就表明了成功

回調該函數未必就表明了成功

若是訪問的頁面服務器出錯(返回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];
    }
}

4-2.加載失敗

2種請求錯誤:

  1. 在「頁面開始請求」後 「收到請求響應」前的錯誤

好比:地址非法,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:
  1. 在請求頁面過程當中的錯誤

    服務器接收到請求,並開始返回數據給到客戶端的過程當中出現傳輸錯誤

    這個錯誤不是返回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:

5.安全驗證/證書驗證

對訪問網站的證書作驗證,並決定是否攔截

實際應用過程當中因爲涉及到第三方合做,因此基本採用所有放過+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

3、訪問頁面

請求方式

1.請求在線頁面

//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"];

2.請求沙盒頁面

請求本地沙盒裏的頁面,主要是拼對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也能夠訪問本地沙盒,並無權限問題,可是爲了減小兼容問題,仍是選擇使用本地讀取

3.請求內置包(bundle)頁面

內置包就是bundle包,就是將bundle包路徑拼接好,而後請求沙盒方式讀取頁面

自定義了一個url頭部"bundle://",在請求的時候作"file:///"頭部替換

4.加載源代碼

直接把html文件讀出來之後,以頁面內容方式去讀取

[self.wkWebView loadHTMLString:htmlString baseURL:nil];

5.離線資源包的一點思考

利用離線加載這一特性,咱們能夠經過服務端資源打包成本地資源包(zip包),經過服務器比對方式下載資源包,解壓後放在本地指定的沙盒目錄,隨後經過wkwebview加載本地方式打開頁面。

對於資源包要求

  1. 先後端分離(目前前端基本如此)
  2. 資源包加載須要相對路徑,大部分在線資源都是經過cdn的,如何經過cdn去轉換成資源包並打包進來,也是一個挑戰,或者直接用cdn包也是能夠的
  3. 要考慮降級策略,若是加載失敗,資源包出現問題,如何快速替換最新資源包或者回滾。

緩存策略

typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    NSURLRequestUseProtocolCachePolicy = 0, // 默認策略,具體的緩存邏輯和協議的聲明有關,若是協議沒有聲明,不須要每次從新驗證cache。
    NSURLRequestReloadIgnoringLocalCacheData = 1, // 忽略本地緩存,直接從後臺請求數據
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // 忽略本地緩存數據、代理和其餘中介的緩存,直接從後臺請求數據
    NSURLRequestReturnCacheDataElseLoad = 2, // // 優先從本地拿數據,且忽略請求生命時長和過時時間。可是若是沒有本地cache,則請求源數據
    NSURLRequestReturnCacheDataDontLoad = 3,  //只從本地拿數據 離線模式
    NSURLRequestReloadRevalidatingCacheData = 5, // 從原始地址確認緩存數據的合法性後,緩存數據就可使用,不然從原始地址加載。
};

1.默認策略NSURLRequestUseProtocolCachePolicy

遵循web的緩存策略,簡單介紹:

分爲兩種緩存

1.對比緩存 (服務器方式比對,304)

須要和服務器作一次比對,可是不會拿回全部數據,因此請求快且輕。

Etag / If-None-Match :返回Etag給到客戶端,下次請求時header中將etag的值設置在If-None-Match 服務器作比對後客戶端比較後,決策是否緩存

image-20200604163300511

Last-Modified / If-Modified-Since:原理相似上面,只不過是用時間的新舊來決策緩存

image-20200604163514314

2.強緩存 (本地緩存,200 from memory cache/from disk cache)

Expires(1.0產物,基本能夠忽略) 第一次請求返回一個head,值是一個時間點,下次若是再請求相同資源,判斷時間是否過時,若是未過時則命中緩存

Cache-Control,主要指定max-age={xxx sencods}

image-20200604164303844

2.NSURLRequestReloadIgnoringLocalCacheData

忽略全部緩存,建議本地加載能夠採起這種方式,忽略緩存,由於緩存空間是有限的,不要影響真正須要緩存的頁面

關於我

期待與要求上進的您進一步溝通

微信號:maako127

掃描下方二維碼加入個人公衆號(二碼前端說),按期更新前端相關技術乾貨

icon_gongzhonghao

相關文章
相關標籤/搜索