注意:WKHTTPCookieStore 为示意,画到了 Networking 进程,实际情况中此模块分散在 WebContent、Networking 以及 UI Process 中,且各进程中的部分通过 IPC 桥接。
根据上图可以引导出 WKWebView Cookie 相关的 2 个核心点:
1)WKWebView Cookie 问题具体是什么
- 对于 "Session Cookie":App 进程与 WKWebView 进程(WebContent Networking)之间 完全隔离。
- 对于 "持久化 Cookie":App 进程与 WKWebView 进程(WebContent Networking)之间 同步存在时差。
2)造成 WKWebView Cookie 问题的根本原因
- App 进程 与 Networking 双进程的设计。
核心目标
在了解 WKWebView 问题以及对应的根本原因之后,如何来处理此问题相对也清晰了:根据是否采用代理了 WKWebView 的网络请求,我们需要不同的处理策略。
- 场景 1 - 未代理 WKWebView 网络请求:Cookie 完全由 Networking 进程管理,WKWebView 内可自闭环。大部分情况下 App 进程也无需感知,如果确实需要感知,可以根据业务场景选择 JS 桥接、强制持久化等方案。
- 场景 2 - 已代理 WKWebView 网络请求:Cookie 大部分是由 App 进程来管理,此时应该采用何种同步策略。
由于场景 1 中我们并未在生产环境中采用,故本文不打算做冒然分析。下面主要聚焦于场景 2 来做进一部分分析。在场景 2 下我们的核心目标:
- 对于 App 进程中产生的 Cookie,能够及时同步到 Networking 进程:主要解决 Reponse 中存在 "Set-Cookie" 情况下,JS 端如何及时读取相关 Cookie 的问题。
- 对于 WebContent 中由 JS 产生的 Cookie,能及时同步到 App 进程:主要解决在 JS 端产生 Cookie 之后,我们如何保证在后续代理的网络请求中可被正常携带的问题。
同步手段
在确认方案之前,我们首先要搞清楚一个问题:客户端侧 Cookie 来源有哪些?
对于 App 进程,Cookie 来源有两个:
- 通过 NSHTTPCookieStorage 写入的。
- 在网络请求 Response Header 中通过 "Set-Cookie" 写入的。
对于 WebContent 进程,主要是 JS 通过 document.cookie 写入的(网络代理之后 Set-Cookie 不会在 WKWebView 进程中生效)。
其次,我们要确认可用做同步的手段有哪些:
对于 iOS 11 之后的系统,苹果已经为我们提供了 WKHTTPCookieStore 对象用来读写、监听 WKWebView 对应的 Cookie,可以直接使用。
对于 iOS 11 之前的系统,需要区分处理一下。
从 App 进程同步到 Networking 进程,简单流程如下:
- 第1步,需要把 Session Cookie 持久化,临时保存(注意需要标识,以供恢复)。
- 第2步,调用 NSHTTPCookieStorage 内部接口 _saveCookies 触发强制同步。
- 第3步,恢复临时保存的 Session Cookie,避免污染。
由于 Networking 进程不会产生 Cookie,所以我们下面要做的是从 WebContent 进程同步 Cookie:处理策略即在 JS 侧重写 document.cookie 方法,在 JS 修改 cookies 时,通过 bridge 将 cookie 传递到 App 进程。
处理方案
在理清楚问题、目标和可用手段之后,我们即可总结出 WKWebView Cookie 相关问题的处理方案:
- 对于 iOS 11 及之后的系统,我们可以通过 HOOK NSHTTPCookieStorage 中读写 Cookie 的接口,并且监听网络请求中 "Set-Cookie" 关键字,在 App 进程 Cookie 发生变化时同步到 WKWebView;同时通过 WKHTTPCookieStore 提供 cookiesDidChangeInCookieStore 能力来监听 WKWebView 中 Cookie 的变化。
- 对于 iOS 11 之前的系统,处理策略类似。但是我们需要过 NSHTTPCookieStorage 接口来做强制同步,并且需要注意恢复 Cookie 的 SessionOnly 属性;同时需要通过在 JS 侧重写 document.cookie 的方式,来感知 WKWebView 中 Cookie 的变化。
特别注意:
采用 iOS 11 之后方案处理时一定要注意,对 WKHTTPCookieStore 的操作会 涉及到 IPC 通信,如果通信过于频繁、通信数据量过大,会产生明显的性能问题。极端情况可能造成 IPC 模块异常,出现所有 WKWebView 都无法加载的情况。比如典型的场景,如果一个页面请求较多、每个请求都带"Set-Cookie"、且业务上为了简单,每次把 App 进程的 Cookie 全量同步到 WKWebView,则 Cookie 过多时,有一定概率(暴力测试可复现)触发 IPC 异常,导致后续所有 WKWebView 实例都无法正常加载,只有 App *进程才可恢复。建议在同步 Cookie 时,尽量按需同步变化的部分。
3 全面屏适配问题
全面屏适配问题相对不复杂,但因为 WKWebView 如 UIWebView 在表现上的不同,导致容易造成一些困扰。
问题是 UIWebView 与 WKWebView 在对前端 viewport-fit 支持表现上略有差别:UIWebView 对 viewport-fit 支持度较好,表现基本与官方文档表述一致。但是 WKWebView 中存在一个潜规则,如果 Web 页面内 body 的高度,在没有超出 WKWebView 组件实际高度时,viewport-fit=cover 可能不生效。
处理办法是在页面中规避掉此类情况,如配置 body height 为 100vh (或其它类似方案)。
4 WebContent 进程崩溃问题
这是一个出现概率不高,但是缺乏通用、有效解决办法的问题。我们知道 WKWebView 多进程模式下,如果 WebContent 进程因为各种原因出现 Crash,则 WKWebView 会通过 webViewWebContentProcessDidTerminate 回调告诉开发者,一般情况下我们会通过 reload 方法重新加载页面。同时如果用户设备内存紧张,则可能出现系统主动 KILL WebContent 进程的情况。即可能出现 App 进程(前台)正常,但是 WebContent 崩溃、页面重新加载的情况。
绝大部分情况,进入此流程并不一定会对用户操作造成困扰。但是,如果此时造成内存紧张是因为前端触发业务导致,典型如表单中唤起相机上传图片,此流程对用户的影响可能是致命的。即使我们通过 WebView reload 使页面恢复,用户在执行的上传动作也会被打断,导致提交流程出现异常、影响用户的操作。且如果用户设备进入此状态,大部分情况下用户再次操作还会触发同样的流程。
这种情况下,用户无法及时感知到造成问题的根本原因,绝大多数直观反应即为:“App 出现 bug 了!”故从用户角度来看,缺少自动恢复、处理问题的办法。
目前此问题缺乏有效、统一解决办法,一种解决思路是客户端与前端配置,针对核心、可能出现异常的流程,定向设计解决方案。通过端侧的能力来将数据持久化,在类似异常发生之后使用持久化数据恢复现场,尽量在用户无感的情况下保证用户操作流程正常。
四 总结以上便是我们在端容器设计开发过程中,WKWebView 使用上遇到的一些典型问题和对应的解决办法。总的来说,目前造成这么不协调的状态,大部分是因为系统平台未能充分考虑开发者诉求、组件设计对历史业务兼容性不佳导致的。当然,现在这种状态必然也不是一种合理状态,未来无论是系统平台方、还是业务方、或者是开发者,当矛盾无法协调时总有一方要进行妥协。在这个时间点来临之前,希望上面总结内容,能够为受此类问题困扰的同学提供一些帮助。
作者 | 驽良
原文链接:http://click.aliyun.com/m/1000285318/
本文为阿里云原创内容,未经允许不得转载。
,