最近遇到这么一件事儿,网页在个别中国移动4G网络下,访问白屏
环境:iOS 14 部分移动4G网络
问题: WKWebView, 回调commitNavigation 之后,一直在等待,didFinish一直执行不到。
经过测试,定位到 script位置,script使用了一个并不存在的网络地址,放到wkWebview中就导致了页面一直在加载,但是加载不出来,直到网页超时(一般30-60秒)
html代码如下(伪代码)
<header>
<script src="https://some.url.cannot.find.js"></script>
</header>
测试发现,就算在iphone的safari中,直接访问一个不存在地址,safari地址栏下的进度条会一直在读条,持续进5分钟才会提示无法链接。
为了解决这个问题
其实这个是中国移动的问题,他通过dns没找到,那就报错呗,他就不,任性
提出了几种方案
- 拦截h5页面,提前处理掉有问题的h5
- 拦截相关请求,直接返回失败。
- H5改
- 方案1: 失败!
在webView request的之前,通过AFNetWorking直接请求H5的text,然后替换文字,替换后直接使用
[aWebView loadHTMLString:@"替换后的htmlString" baseURL: baseURL];
但是问题出现了:因为变成了本地的string,因此html里面一些相对地址的资源文件都丢失了,如 图片、css、js等等。
如果都转成本地,那么代价就太大了
因此这边想了一个办法,拦截相关的请求
*方案2:
wkWebView提供了WKURLSchemeHandler供我们拦截请求,但不允许拦截http https ftp等,但是可以通过Category 重写系统方法来实现,具体做法百度知道。
也发生了个bug,百度提的不多,就是拦截的请求回丢失body,导致其他的相关请求都失败了! 失败的代码如下:
if ([urlSchemeTask.request.URL.absoluteString isEqualToString:@"https://some.host.com/www/cordova.js"] || [urlSchemeTask.request.URL.absoluteString isEqualToString:@"https://some.host.com/www/js/index.js"]) {
NSError * error = [NSError errorWithDomain:NSURLErrorDomain code:404 userInfo:@{NSLocalizedDescriptionKey: @"request unsupportted js content"}];
[urlSchemeTask didFailWithError:error];
}
解决问题的方法也很简单,就是使用AF,直接请求网页数据,通过WKURLSchemeHandler提供的代理方法传递给webView,做法与拦截自定义sheme后,加载本地资源的过程如出一辙,代码如下:
if ([urlSchemeTask.request.URL.absoluteString isEqualToString:@"https://some.host.com/www/cordova.js"] || [urlSchemeTask.request.URL.absoluteString isEqualToString:@"https://some.host.com/www/js/index.js"]) {
NSError * error = [NSError errorWithDomain:NSURLErrorDomain code:404 userInfo:@{NSLocalizedDescriptionKey: @"request unsupportted js content"}];
[urlSchemeTask didFailWithError:error];
} else {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlSchemeTask.request.URL];
AFURLSessionManager * manager = [[AFURLSessionManager alloc]initWithSessionConfiguration:NSURLSessionConfiguration.defaultSessionConfiguration];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSURLSessionDataTask * task = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
NSData * data = (NSData *)responseObject;
[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
}];
[task resume];
}
- 方案3: 最好了,我就不说了。