【iOS】WKWebView使用Cookies遇到的坑

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了【iOS】WKWebView使用Cookies遇到的坑相关的知识,希望对你有一定的参考价值。

参考技术A Apple推出WKWebView已经有一段时间了,相对于UIWebView而言,内存占用只有UIWebView的一半左右,但是响应速度和效率上却是UIWebView的两倍。
  总结WKWebView使用方法的帖子文章很多,这里不再赘述,这里重点总结一下cookies共享问题。

由于以上原因,导致WKWebView无法与App自身的Cookies、UIWebView之间共享Cookies数据。WKWebView这样做也有一定的好处,在不用操作原有Cookies的基础上,独立的一套Cookies,有效的防止了Web与App Api接口的Cookie相互污染。
  但是在使用过程中也遇到了一些坑。

有的时候因为业务需求,就是需要相互共享Cookies,这样的案例也有很多,支付宝中大量的Web中,应该就有很多Cookie是和App共享的吧。

在WkWebView接收到Response后,将Response带的Cookies取出,然后直接放入[NSHTTPCookieStorage sharedHTTPCookieStorage] 容器中:

然后在完全加载完成后:

为什么在完全加载完成后需要重新给WKWebView设置Cookie呢?如果你不这样做的话很有可能因为a标签跳转,导致下一次跳转的时候Cookie丢失。

上面的方法可以将服务器Set-Cookie携带到下一次请求中。但是如果302跳转出现在你的第一次加载并且你使用了下面的方法设置第一次加载的Cookies,那么在302跳转时服务器Set-Cookie将不会被携带到下一次302跳转的目标请求中。

比如:第一次加载https:/a.com/test1,然后加载https:/a.com/test1设置Cookie为a=1,服务器在https:/a.com/test1中Set-Cookie a=2;然后302跳转到https:/a.com/test2,这个时候会发现https:/a.com/test2中获取到的a还是1,Set-Cookie并没有成功。

1.加载一个本地为空的html,域名指向你的第一次加载的url的域名。

2.通过以下方法,在第一次加载完成后,将需要设置的Cookies设置到WKWebView中,因为是加载的本地的html以下方法会立即执行。

按道理来说每个WKWebView都有一个单独的存储Cookies的空间,相互不影响,但是,奇妙之处就是我在一个UIViewController中生成了一个WKWebView,然后进行了一系列的网络访问后,推出并销毁这个UIViewcontroller;在下次进来的时候这个WKWebView会携带上次访问的部分Cookies。
  这个原因是WKWebView会将Cookie存储到沙盒目录的文件中,下次WKWebView被实例化的时候,会去同步这个文件中的Cookies,如果不希望它去同步之歌Cookies,那么直接删掉好了。

不足之处,欢迎轻轻地喷一下......

[iOS]使用WKWebView遇到的问题总结

参考技术A 在使用WKWebView获取userAgent的时候, 如果要全局配置, 使所有的WKWebView都能生效, 我们可能的做法是在AppDelegate中来配置, 但是需要一个WKWebView实例对象, 所以可能是这么写的:

这样, 你会发现设置一直是无效的, 在回调里打印一下:

这时发现获取到的 info 字段为nil, 并且error信息如下:

个人猜测: 这是因为, WKWebView的evaluateJavaScript方法是异步执行的, 当WKWebView回调这个方法的时候, 其实例对象已经从内存中释放了, 所以导致回调出错.
我做了如下验证, 在回调方法里输出webView实例对象:

会发现输出是info有值, 而error为nil, webView有值, 又正常了, 有人说了,看样子不是这个问题! 真的么? 仔细想一下会发现, 在webView的方法回调闭包里使用了webView实例, 会发生什么? 对, 循环引用! webView实例此时不为nil, 这也验证了, 如果webView实例正常的话, 获取结果是不会有误的! 继续上面的验证, 我们弱引用一下:

这时, 会发现: info和webView都为nil, error值为上面那个错误!!!

这样, 基本验证出现这个问题的原因是: webView 提前释放了!

但是为了添加这个设置, 而将webView 设为全局变量, 仿佛有点得不偿失, 这时可以在使用webView的页面进行设置, 或者使用UIWebView替换:

在做JS与原生交互的时候, 使用下面方法注入的协议无效:

然后在js端使用的时候: 这里不需要传参数, 直接这么写的

这样, 没有响应js端的事件!!!
在代理方法中:

一直没有收到回调!!!
其实, 并不是注入协议失败, 这么使用也没问题, 问题就出在postMessage的参数上, 如果是带参数的:

这么写, 是完全没有问题的, 所以如果不需传参数的话, 可以这么写:

给一个空的字典, 就能正常交互了!!!

在WKWebView加载的HTML页面上, 如果长按会弹出一些选择框, 在文字上长按, 会弹出UIMenuController选择框:

而在图片上长按, 会弹出一个alertSheet:

这里可以保存图片到系统相册(如果有权限), 或者复制到剪切板. 但是这些需求并不是我们需要, 如何禁止这些行为呢?需要从JS入手, 只需要执行下面两句js即可:

可以在创建WKWebView的时候注入:

也可以在页面加载完成后的代理方法中执行:

在加载的HTML页面中, 无端出现一个广告的悬浮框:

打开之后是这样的:

而且只会在移动4G网络下才会出现, 其实这是移动的流量劫持, 强加的广告推广,目前网上有一些解决方式,常用的有:

其他的可参考这篇文章 iOS 客户端对于运营商劫持的一点点对抗方式


在联调的时候, 前端的同学改了一些东西, 例如页面的布局, 显示元素, 或者js方法, 而APP端没反应!!!

这是因为, WKWebView有缓存, 为了保证每次加载的都是最新的页面, 可以在加载的链接后面加上一个时间戳, 例如你的HTML地址为:

一般使用是这样的:

这样的话是有缓存, 加载一次之后, 再去加载也不是最新的页面, 可以这样使用:

这样每次加载的时候都会是最新的, 当然弊端就是, 每次都会耗费一些额外的流量.

在页面无导航的情况下,系统会自动调节滚动视图的contentInset,使其视图永远处于状态栏之下,但是如果我们想让滚动视图的Y坐标从屏幕顶端(状态栏)开始,我们都知道怎么修改,但是 WKWebView不是继承自UIScrollView 的,所以不能直接设置,可以这么写:

这个闪退发生在与 JS 进行交互,使用下面的方法注册协议时:

如果重复注册了相同名称的协议,就会发生闪退,所以在使用完webView的时候,一定要记得移除已注册的协议:

app首页使用 WKWebView 来承载的内容,在启动时,如果添加了引导页/广告页,这时如果有手势操作,例如在出现广告页时点击屏幕,就会闪退,并在控制台输出:

添加一个全局断点,调试发现崩溃信息为

查了些资料,没找到具体原因,但是了解到和 +load方法有关,我是在 +load 方法内初始化的广告页的信息,把这些放在 didFinishLaunchingWithOptions 进行初始化,就不会有这个问题;

解决:
将广告/引导页视图的初始化放在 didFinishLaunchingWithOptions 方法内。

以上是关于【iOS】WKWebView使用Cookies遇到的坑的主要内容,如果未能解决你的问题,请参考以下文章

[iOS]使用WKWebView遇到的问题总结

在 iOS 12 中设置 WKWebView cookie 接受策略

在 iOS 9 中遇到 WKWebview 和 UIWebView 问题

WKWebView强大的新特性

如何使用 iOS 的 WKWebView 的 callAsyncJavaScript 方法?

使用WKWebView替换UIWebView