使用WKWebView 遇到的问题

参考:
WKWebView 那些坑
WKWebView使用指南
WKWebView 是由Apple从iOS 8 开始提供的Web框架。用于替代UIWebView框架。WKWebView才是未来。
WKWebView主要的升级阶段是iOS 9 提供了新的加载本地API。iOS11以后开放了WKHTTPCookieStore 类用于管理webView Cookie。
具体的使用就不介绍啦。补之前遗漏的笔记。在使用过程中遇到的问题。

加载本地HTML

  • iOS 8
    项目属于混合类APP,在APP中沙盒内置了H5资源。但是在iOS 8 中无法访问Document和Library下的资源。如果想要在iOS 8 中加载本地资源。

方案一:(项目使用方案)放在Tmp目录下,用

- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;

方式加载H5资源。Tmp目录存在被系统清理的风险。可以先存一份在Document目录加载时拷贝一份到Tmp目录。

方案二:直接使用UIWebView

方案三:(未验证,补这篇时已找不到之前看的文档,做笔记是好习惯)在Document下开启一个服务,利用服务方式加载HTML

  • iOS 9
    在iOS 9以上版本提供了新api用于加载本地H5资源
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));

readAccessURL 为H5资源的文件的根据目录,不能用html的相对目录,相对目录会出现无法加载js、css等文件。
例: ~/a/b/H5资源
则readAccessURL为~/a/b

如果HTML资源存放在项目bundle文件下可以采用

- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;

方式加载。
js、css最好放在同级目录下
baseURL 可以设置为 [NSBundle mainBundle].resourceURL

白屏问题

项目开发中遇到了白屏问题按照WKWebView 那些坑方式确实可以解决90%以上的白屏问题。
但是项目中在iPhone 6 iOS10.3.3手机上在加载一个较大的页面后,退到后台,开启其他APP,手机整体占用内存较高的情况下再次回到APP偶现白屏的情况。

方案:

经过多次测试发现在遇到这种情况的时候WebView的URL为空。目前方案是利用KVO方式监听WebView的URL变化当URL为空的时候重新reload。

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
   if([keyPath isEqualToString:@"estimatedProgress"])
   {
       self.estimatedProgress = [change[NSKeyValueChangeNewKey] doubleValue];
   }
   else if([keyPath isEqualToString:@"title"])
   {
       self.title = change[NSKeyValueChangeNewKey];
   }else if ([keyPath isEqualToString:@"URL"]) {
       NSURL *newUrl = [change objectForKey:NSKeyValueChangeNewKey];
       NSURL *oldUrl = [change objectForKey:NSKeyValueChangeOldKey];
       if ([newUrl isKindOfClass:[NSNull class]] && ![oldUrl isKindOfClass:[NSNull class]]) {
           [self reload];
       }
   }
}

Cookie 问题

在WK上要处理Cookie主要分为iOS 11 之前和之后。其他很多的论坛或技术分享中已经有很多帖子介绍如何解决Cookie的问题。思路都差不多。但是还是需要结合项目本身问题进行解决。

iOS 11 之前

-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    BOOL isNavigator = YES;
    NSDictionary *headerFields = navigationAction.request.allHTTPHeaderFields;
    // 判断请求属于http和https 请求
    if ([navigationAction.request.URL.absoluteString hasPrefix:@"http"]) {
        NSString *cookie = headerFields[@"Cookie"]; // 请求头中是否包含cookie
        if (cookie == nil && ![navigationAction.request.URL.host isEqualToString:self.oncHost]) {
            self.oncHost = navigationAction.request.URL.host;
            NSMutableURLRequest *urlRequest = [navigationAction.request mutableCopy];
            urlRequest.allHTTPHeaderFields = headerFields;
            [urlRequest addValue:[webView phpCookieStringWithDomain:urlRequest.URL.host] forHTTPHeaderField:@"Cookie"];
            [webView loadRequest:urlRequest];
            isNavigator = NO;
        }else{
            isNavigator = YES;
        }
    }
    if(isNavigator) {
        self.currentRequest = navigationAction.request;
        if(navigationAction.targetFrame == nil) {
            [webView loadRequest:navigationAction.request];
        }
        decisionHandler(WKNavigationActionPolicyAllow);
    }else {
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    return;

- (NSString *)phpCookieStringWithDomain:(NSString *)domain
{
    @autoreleasepool {
        NSMutableString *cookieSting =[NSMutableString string];
        NSArray *cookieArr = [self sharedHTTPCookieStorage];
        for (NSHTTPCookie *cookie in cookieArr) {
            if ([cookie.domain containsString:domain]) {
                [cookieSting appendString:[NSString stringWithFormat:@"%@ = %@;",cookie.name,cookie.value]];
            }
        }
        if (cookieSting.length > 1)[cookieSting deleteCharactersInRange:NSMakeRange(cookieSting.length - 1, 1)];
        
        return (NSString *)cookieSting;
    }
}

iOS 11 及之后版本

在使用UIWebView时,所有Cookie由NSHTTPCookie对象进行管理。
在iOS 11以后WK提供了WKHTTPCookieStore对象进行Cookie管理,但是NSHTTPCookie和WKHTTPCookieStore的Cookie不共享。在iOS 11以上可以采用将NSHTTPCookie Cookie同步给WKHTTPCookieStore对象。解决Cookie不同步问题。

NSHTTPCookieStorage * shareCookie = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in shareCookie.cookies) {
    [cookieStore setCookie:cookie completionHandler:nil];
}

localstorage 存储延迟问题

在WK中打开一个新的页面读取上个页面的localstorage数据。localstorage读取的还是旧的值。
在官方文档中:
The process pool associated with a web view is specified by its web view configuration. Each web view is given its own Web Content process until an implementation-defined process limit is reached; after that, web views with the same process pool end up sharing Web Content processes.
每个web页面都有自己的进程池,具有相同进程池的web会进行共享进程。

方案:

创建WKProcessPool单例进行存储数据

+ (WKProcessPool *)onceProcessPool {
    static WKProcessPool *processPool;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        processPool = [[WKProcessPool alloc] init];
    });
    return processPool;
}

H5 交互问题

UIWebView与H5 交互代码 同步方式

- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

WK与H5交互代码 异步方式

- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

WK如果想要达到同步调用方式可以采用卡住当前线程的方式利用runloop。目前只有设置userAgent用到同步方法。iOS9之后已经提供了设置userAgent的方法。

@property (nullable, nonatomic, copy) NSString *customUserAgent API_AVAILABLE(macosx(10.11), ios(9.0));

在和H5交互时采用同步方式会导致死锁。

__block BOOL isExecuted = NO;
[self.webView evaluateJavaScript:javaScriptString completionHandler:^(id obj, NSError *error) {
        result = obj;
        isExecuted = YES;
}];
        while (isExecuted == NO) {
       [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

当和H5交互时会导致死锁而卡死。因为H5是单线程执行在H5调用Native代码后需要等待Native回调继续往下执行。而Native调用evaluateJavaScript:completionHandler:执行了H5的代码后需要在block中等待H5的执行结果,再执行后续操作。双方相互等待造成了死锁效应。目前项目所有与H5的交互采用异步方式。如果有解决方案欢迎评论或私信。

其他问题

项目架构设计是每个版本内置了一个初始资源。如果有新资源更新及时更新并更新沙盒资源。由于沙盒存储路径是每次更新后会叠加版本。(初始资源路径100/html。更新后资源路径101/html)。在用户使用过程中,更新了资源。需要重新刷新当前页面。

[webView reload];

在执行reload方法后已经是加载的的新资源的路径。但是无法正常reload和加载,一直加载失败。而且存在真机和模拟器的差异。真机无法加载,模拟器可以正常加载。重启后正常加载。目前解决方案是初始化一个新的webView容器,并添加到控制器中。再将之前的webView给移除。

- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

如有错误,欢迎指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容