wkwebview的cookie同步、alert不弹出等问题

======================cookie===========================================
iOS8 以后推出了 WKWebview,显示更快效率更高.

但是 WKWebview 有几个很坑的问题

1.Cache 与系统分开,并且在 iOS8上面无法清除(iOS9增加了相关方法)

2.Cookie 与系统分开,web 的 cookie 与native 的 cookie 是分开的.

3.不走 NSURLProtocol, 无法自定义网络请求.

So, 需要 native 与 web 统一 cookie 就无从谈起了,甚至 webview 于 webview 之间的 cookie 同步也有问题.

一.先来解决 webview 与 webview 之间的同步问题.

如果使用两个以上 webview 同时访问服务器,这两个 webview 之间的存储是毫无关系的.最关键的是,保存登录状态的 sessionid 也不一样,也就是说,在一个 webview 页面里登录之后,另一个 webview 依旧是未登录的状态.

这个比较容易处理,让两个 webview 使用同一个 WKProcesspool 就可以了.


同一个app中,多个页面使用wkwebview,在一个页面登录,其他页面无需再登录,由于使用wkwebview其cookie不同步,所以会出现在一个页面登录后还需在另一个页面重新登录的情况。上代码吧!
注意:注意:注意:所加载页面必须为【登陆后】的页面。


创建单例类.h

#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>

@interface WKCookieSyncManager : NSObject

@property (nonatomic, strong) WKProcessPool *processPool;

- (void)setCookie;

+ (instancetype)sharedWKCookieSyncManager;

@end

.m

#import "WKCookieSyncManager.h"

@interface WKCookieSyncManager () <WKNavigationDelegate>
@property (nonatomic, strong) WKWebView *webView;
///用来测试的url这个url是不存在的
@property (nonatomic, strong) NSURL *testUrl;
@end
@implementation WKCookieSyncManager

+ (instancetype)sharedWKCookieSyncManager {
    static WKCookieSyncManager *sharedWKCookieSyncManagerInstance = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedWKCookieSyncManagerInstance = [[self alloc] init];
    });
    return sharedWKCookieSyncManagerInstance;
}
- (void)setCookie {
    //判断系统是否支持wkWebView
    Class wkWebView = NSClassFromString(@"WKWebView");
    if (!wkWebView) {
        return;
    }
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    config.processPool = self.processPool;
    self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:self.testUrl];
    self.webView.navigationDelegate = self;
    [self.webView loadRequest:request];
}

#pragma - get
- (WKProcessPool *)processPool {
    if (!_processPool) {
        static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            _processPool = [[WKProcessPool alloc] init];
        });
    }
    return _processPool;
}
- (NSURL *)testUrl {
    if (!_testUrl) {
        NSURLComponents *urlComponents = [NSURLComponents new];
        urlComponents.host = @"oam.yixiangdai.com";
        urlComponents.scheme = @"http";
        urlComponents.path = @"/tsttsssdsds.aspx";
        NSLog(@"测试url=%@", urlComponents.URL);
        //一个不存在 的URl
        return urlComponents.URL;
    }
      return _testUrl;
}

//交互js(略过)
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    //取出cookie
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    //js函数
    NSString *JSFuncString =
    @"function setCookie(name,value,expires)\
    {\
        var oDate=new Date();\
        oDate.setDate(oDate.getDate()+expires);\
        document.cookie=name+'='+value+';expires='+oDate;\
    }\
    function getCookie(name)\
    {\
        var arr = document.cookie.match(new RegExp('(^| )'+name+'=([^;]*)(;|$)'));\
        if(arr != null) return unescape(arr[2]); return null;\
    }\
    function delCookie(name)\
    {\
        var exp = new Date();\
        exp.setTime(exp.getTime() - 1);\
        var cval=getCookie(name);\
        if(cval!=null) document.cookie= name + '='+cval+';expires='+exp.toGMTString();\
    }";
    
    //拼凑js字符串
    NSMutableString *JSCookieString = JSFuncString.mutableCopy;
    for (NSHTTPCookie *cookie in cookieStorage.cookies) {
        NSString *excuteJSString = [NSString stringWithFormat:@"setCookie('%@', '%@', 1);", cookie.name, cookie.value];
        [JSCookieString appendString:excuteJSString];
        NSLog(@"----%@",excuteJSString);
    }
    //执行js
    [webView evaluateJavaScript:JSCookieString completionHandler:nil];
}

使用(在创建wkwebview的时候,其实就两句在横线处标注啦):

//懒加载吧
- (WKWebView *)wkWebView{
    if (!_wkWebView) {
 ///--------------------------把 cookiesManager 做成单例模式,每一个需要同步的 WKWebview 都设置这个 processPool.
        WKCookieSyncManager *cookiesManager = [WKCookieSyncManager sharedWKCookieSyncManager];
        //设置网页的配置文件
        WKWebViewConfiguration * Configuration = [[WKWebViewConfiguration alloc]init];
//允许视频播放
        Configuration.allowsAirPlayForMediaPlayback = YES;
        // 允许在线播放
        Configuration.allowsInlineMediaPlayback = YES;
// 允许可以与网页交互,选择视图
        Configuration.selectionGranularity = YES;
/// -------------------------web内容处理池,设置这个processPool
        Configuration.processPool = cookiesManager.processPool;
        //自定义配置,一般用于 js调用oc方法(OC拦截URL中的数据做自定义操作)
        WKUserContentController * UserContentController = [[WKUserContentController alloc]init];


        // 是否支持记忆读取
        Configuration.suppressesIncrementalRendering = YES;
        // 允许用户更改网页的设置
        Configuration.userContentController = UserContentController;
        _wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 10, 100,100) configuration:Configuration];
        
//添加到主控制器上
        [self.view addSubview:self.wkWebView];
        [self webViewloadURLType];//加载的代码(比如下面两行)
//创建一个NSURLRequest 的对象
 //NSURLRequest * inforURL = [NSURLRequest requestWithURL:[NSURL URLWithString:selfURL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];////selfURL为登陆后的URl
//加载网页
//[self.wkWebView loadRequest:inforURL];



        // 设置代理
        _wkWebView.navigationDelegate = self;
        _wkWebView.UIDelegate = self;

        //开启手势触摸
        _wkWebView.allowsBackForwardNavigationGestures = YES;
        // 设置 可以前进 和 后退
        //适应你设定的尺寸
        [_wkWebView sizeToFit];
    }
    return _wkWebView;
}

======================alert不弹出===========================================
WKWebView默认不响应js的alert()事件,如何可以开启alert权限呢?
设置wkwebview.delegate = self;
实现下面三个方法:
onJsAlert :警告框(WebView上alert无效,需要定制WebChromeClient处理弹出)
onJsConfirm : 确定框.
onJsPrompt : 提示框.

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
    
}
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    //    DLOG(@"msg = %@ frmae = %@",message,frame);
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }])];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = defaultText;
    }];
    [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alertController.textFields[0].text?:@"");
    }])];


    [self presentViewController:alertController animated:YES completion:nil];
}

====================对比UIwebview及其他坑=============

WKWebView使用遇到的坑

简介

使用WKWebView一段时间,发现它和UIWebView的一些区别之处,有一写遇到的坑,现在对处理方式做了个小总结,现分享给大家.

区别

1.EvaluateJavaScript方法为异步

  • UIWebview:
    UIWebView中是同步执行的,直接调用
    - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
    方法返回执行结果
  • WKWebView
    WKWebView中,改为了block的方式进行值返回,并且该方法的执行是异步的

延伸:执行JS方法的使用场景之一,就是获取当前webview的title,WKWebView提供了新属性title,如果是想获取title,可以直接使用WKWebView的title属性.

2.cookie设置方式不同

  • UIWebView:

通过该方式设置的,为全局的cookie,项目中任意的UIWebView均携带一样的cookie.设置之后不需要做额外的操作.

  • WKWebView
    网页将不再能获取默认的cookie,如果需要携带cookie,需要做一些操作:
    1 初始化cookie, NSString *cookie = @"document.cookie='cookieKey=cookieValue'";
    2 注入cookie
    获取当前的userContentController:

注入scrpit:
WKUserScript *script = [[WKUserScript alloc] initWithSource:cookieValue injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:script];

注意

注入script时参数indectionTime有两个可选项WKUserScriptInjectionTimeAtDocumentStartWKUserScriptInjectionTimeAtDocumentEnd,
我们看一下官方文档对于这两个选项的解释:
WKUserScriptInjectionTimeAtDocumentStart : 注入时机为document的元素生成之后,其他内容load之前.
WKUserScriptInjectionTimeAtDocumentEnd : 注入时机为document全部load完成,任意子资源load完成之前.
一般情况下,如果想尽早注入cookie,在WKUserScriptInjectionTimeAtDocumentStart时完成即可,但是有一种特殊情况,即目前的诊疗圈为后端渲染,数据请求依赖cookie中的sessionKey,而前端页面的元素依赖后端返回的数据,因此,有一个问题,即cookie是在页面元素生成之后注入的,而在这之前,后端需要获取cookie,那么应该怎么办呢??

在requestHeader内注入cookie

NSString *cookie = @"cookieKey1=cookieValue1;cookieKey2=cookieValue2";

这样在网络请求开始时,requestHeader将携带cookie.

3.WKWebView默认禁止了一些跳转

  • UIWebView
    打开ituns.apple.com跳转到appStore, 拨打电话, 唤起邮箱等一系列操作UIWebView默认支持的.
  • WKWebView
    默认禁止了以上行为,除此之外,js端通过window.open()打开新的网页的动作也被禁掉了.
    如何支持呢?
可以跳转appStore或者拨号

-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if(webView != self.wkWebView) {
decisionHandler(WKNavigationActionPolicyAllow);
return;
}
UIApplication *app = [UIApplication sharedApplication];
if ([url.scheme isEqualToString:@"tel"])
{
if ([app canOpenURL:url])
{
[app openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}
if ([url.absoluteString containsString:@"ituns.apple.com"])
{
if ([app canOpenURL:url])
{
[app openURL:url];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}
decisionHandler(WKNavigationActionPolicyAllow);
}

支持window.open()

需要打开新界面是,WKWebView的代理WKUIDelegate方法

  • (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
    会拦截到window.open()事件.
    只需要我们在在方法内进行处理
    if (!navigationAction.targetFrame.isMainFrame) {
    [webView loadRequest:navigationAction.request];
    }

================wkWebview==========================
个人所遇到的几个问题记录:
1.cookie问题(多种处理方式,比如新建单例类)。
2.点击内部链接无反应问题。
3.点击不弹出js的alert()。
4.AFNetworking存取cookie。
5.双击某处闪退问题。
6.点击要下载的文件无法下载,跳转到Safari处理。
7.交互的其他问题。

2.点击内部链接无反应问题

-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
    NSLog(@"createWebViewWithConfiguration");
//    <pre>
//    a 超连接中target的意思
//      _blank -- 在新窗口中打开链接
//      _parent -- 在父窗体中打开链接
//      _self -- 在当前窗体打开链接,此为默认值
//      _top -- 在当前窗体打开链接,并替换当前的整个窗体(框架页)
//    </pre>
    ////**********      点击内部链接无反应问题    ************
    //假如是重新打开窗口的话
    if (!navigationAction.targetFrame.isMainFrame) {
        [webView loadRequest:navigationAction.request];
    }
    return nil;
}

参考:https://www.jianshu.com/p/7e150bf7967f

3.不弹出问题

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
    
}
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    //    DLOG(@"msg = %@ frmae = %@",message,frame);
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }])];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = defaultText;
    }];
    [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alertController.textFields[0].text?:@"");
    }])];


    [self presentViewController:alertController animated:YES completion:nil];
}

======================其他===========================
展示一个网页,但是需要隐藏一部分页面

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {

    // 在HTML标签都加载完成后,开始处理HTML标签,调用JS,操作document
    //移除网页中的footer。
//    NSString *doc = @"document.getElementById('footer').remove();";
//    [self.wkWebView evaluateJavaScript:doc
//                     completionHandler:^(id _Nullable htmlStr, NSError * _Nullable error) {
//                         if (error) {
//                             NSLog(@"JSError----:%@",error);
//                         }
//                         NSLog(@"html----:%@",htmlStr);
//                     }] ;


    //解决长按tabbaritem弹出alert问题。
//    NSString *changAnAlertQ = @"document.documentElement.style.webkitTouchCallout='none';";
//    [webView evaluateJavaScript:changAnAlertQ completionHandler:^(id _Nullable htmlStr, NSError * _Nullable error) {
//
//    }];
}

WKWebView返回某个历史页面


- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//WKWebView返回某个历史页面
    if (navigationAction.navigationType==WKNavigationTypeBackForward) {
        //判断是返回类型
        if (webView.backForwardList.backList.count>0) {
            //得到栈里面的list
            WKBackForwardListItem * item = webView.backForwardList.currentItem;
            //得到现在加载的list
            for (WKBackForwardListItem * backItem in webView.backForwardList.backList) {
                //循环遍历,得到你想退出到 webView.backForwardList.backList[0]
                //添加判断条件
                [webView goToBackForwardListItem:backItem];
            }
        }
    }
decisionHandler(WKNavigationActionPolicyAllow);
    
}

=================================================
来源(cookie):http://blog.csdn.net/ccwf2006/article/details/53173489
来源(alert):http://www.cnblogs.com/n1ckyxu/p/5587722.html
http://blog.csdn.net/j_av_a/article/details/52160413
另外:webview的离线缓存参考http://blog.csdn.net/horisea/article/details/53815596(不适用于WKWebview)。

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

推荐阅读更多精彩内容