前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >​# iOS WKWebView适配(基础篇)

​# iOS WKWebView适配(基础篇)

原创
作者头像
用户7498074
修改2020-06-24 17:13:08
3.6K0
修改2020-06-24 17:13:08
举报

一、初始化

1.initWithFrame:configuration

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

2.WKWebViewConfiguration类说明

wkwebview初始化时的参数配置

websiteDataStore

wkwebview的存储空间,一般是处理cookie,缓存等浏览器相关的临时存储

读取cookie代码

[config.websiteDataStore fetchDataRecordsOfTypes:[NSSet<NSString *> setWithObject:WKWebsiteDataTypeCookies] completionHandler:^(NSArray<WKWebsiteDataRecord *> *records) {}];	

清理所有存储(allWebsiteDataTypes)

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 属性

processPool

就是一个处理池,打开一个webview可以指定从什么池子里打开,一般用默认或者指定一个单例WKProcessPool就行了

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整个生命周期的代理方法,详细见"二、生命周期方法"

二、生命周期方法(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

三、访问页面

请求方式

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
image-20200604163300511

Last-Modified / If-Modified-Since:原理类似上面,只不过是用时间的新旧来决策缓存

image-20200604163514314
image-20200604163514314

2.强缓存 (本地缓存,200 from memory cache/from disk cache)

Expires(1.0产物,基本可以忽略) 第一次请求返回一个head,值是一个时间点,下次如果再请求相同资源,判断时间是否过期,如果未过期则命中缓存

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

image-20200604164303844
image-20200604164303844
2.NSURLRequestReloadIgnoringLocalCacheData

忽略所有缓存,建议本地加载可以采取这种方式,忽略缓存,因为缓存空间是有限的,不要影响真正需要缓存的页面

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、初始化
    • 1.initWithFrame:configuration
      • 2.WKWebViewConfiguration类说明
        • websiteDataStore
        • allowsInlineMediaPlayback
        • processPool
        • applicationNameForUserAgent
        • mediaTypesRequiringUserActionForPlayback(iOS10+)/mediaPlaybackRequiresUserAction(iOS10-)
        • preferences
        • 其它参数
      • 3.WKPreferences类说明
        • javaScriptEnabled
        • KVC设置 allowFileAccessFromFileURLs
      • 4.WKUIDelegate
        • 5.WKNavigationDelegate
        • 二、生命周期方法(WKNavigationDelegate)
          • 1.请求前决定是否要跳转
            • 2.页面开始请求
              • 3.收到响应后决定是否跳转
                • 4-1.加载完成
                  • 4-2.加载失败
                    • 5.安全验证/证书验证
                      • 其它
                      • 三、访问页面
                        • 请求方式
                          • 1.请求在线页面
                          • 2.请求沙盒页面
                          • 3.请求内置包(bundle)页面
                          • 4.加载源代码
                          • 5.离线资源包的一点思考
                        • 缓存策略
                          • 1.默认策略NSURLRequestUseProtocolCachePolicy
                          • 2.NSURLRequestReloadIgnoringLocalCacheData
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档