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 
#import 

@interface WKCookieSyncManager : NSObject

@property (nonatomic, strong) WKProcessPool *processPool;

- (void)setCookie;

+ (instancetype)sharedWKCookieSyncManager;

@end

.m

#import "WKCookieSyncManager.h"

@interface WKCookieSyncManager () 
@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");
//    
//    a 超连接中target的意思
//      _blank -- 在新窗口中打开链接
//      _parent -- 在父窗体中打开链接
//      _self -- 在当前窗体打开链接,此为默认值
//      _top -- 在当前窗体打开链接,并替换当前的整个窗体(框架页)
//    
////********** 点击内部链接无反应问题 ************ //假如是重新打开窗口的话 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)。

你可能感兴趣的:(wkwebview的cookie同步、alert不弹出等问题)