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}
忽略全部缓存,建议本地加载能够采起这种方式,忽略缓存,由于缓存空间是有限的,不要影响真正须要缓存的页面