WWDC Quick Look 💓 By SwiftGGTeam
Discover WKWebView enhancements

Discover WKWebView enhancements

观看原视频

Highlight

Apple 在 iOS 14 和 macOS Big Sur 中给 WKWebView 加入 content world 隔离、callAsyncJavaScript、页面缩放、PDF/WebArchive 导出、ITP 默认开启和 App-bound domains,让 App 能更安全地把 Web 内容嵌入原生体验。

核心内容

很多 App 起步时会把一个网站放进 UIWebView。页面能显示,业务也能跑,但一旦 App 想控制滚动、拼接多个站点、监听页面事件或导出内容,旧 WebView 的边界就开始变得模糊。网页脚本和 App 注入脚本共享同一个 JavaScript 世界,变量可能互相覆盖,恶意页面也有机会碰到 App 不希望暴露的状态。

这场 session 用一个虚构的 Browser Pets App 串起这些问题。它要把两个内部网站合成一个新闻流,App 负责原生导航、搜索、分享和安全策略,网页继续负责已有内容。迁移到 WKWebView 后,开发者得到一组把 Web 内容纳入原生 App 管理的能力。

Apple 的改动分成五类。第一类是隔离:关闭页面自己的 JavaScript 时,App 仍然能运行自己的脚本;WKContentWorld 还可以把 App 脚本放进单独的 JavaScript 世界。第二类是桥接:callAsyncJavaScript 和带回复的 postMessage 让原生代码和现代异步 JavaScript 之间少写拼字符串和消息追踪代码。第三类是渲染:pageZoom 和自定义 media type 让 App 调整网页表现时少注入 CSS。第四类是导出:查找、PDF、WebArchive 和 macOS 打印补齐了常见文档能力。第五类是隐私:ITP 默认启用,App-bound domains 用白名单限制深度交互的站点。

详细内容

1. 按导航关闭页面 JavaScript

05:58)旧做法是把 WKPreferencesjavaScriptEnabled 设为 false。问题是 WKWebView 里很多 App 交互也靠执行 JavaScript 完成。iOS 14 和 macOS Big Sur 引入 WKWebPagePreferences.allowsContentJavaScript,它只关闭网页内容自带的脚本,App 侧 JavaScript 仍然可用。

API: WKWebPagePreferences.allowsContentJavaScript
Scope: per-navigation web page preferences
Disables: inline scripts, remote JavaScript files, JavaScript URLs
Keeps: JavaScript evaluated by the app
Use case: disable a feed page's custom scrolling script while keeping app logic active

关键点:

  • WKWebPagePreferences 的作用范围是一次 navigation,适合按页面决定脚本策略。
  • allowsContentJavaScript 面向网页内容本身,不会切断 App 和 WKWebView 的脚本交互。
  • session 中的例子是新闻流页面用 JavaScript 自定义滚动,App 关闭它后恢复原生滚动体验。
  • 这个能力适合 App 控制展示层、网页只提供内容的场景。

2. 用 WKContentWorld 隔离 App 脚本

09:03)Browser Pets 遇到的第二个问题是命名冲突。网页里有 commentDetails 函数,App 注入的脚本也用了同名符号,结果网页评论区域变成空白。WKContentWorld 提供独立的 JavaScript sandbox。页面世界代表网页自己的脚本,client world 代表 App 的脚本。

Problem: page JavaScript and app JavaScript both define commentDetails
API: WKContentWorld
Page world: web content's own JavaScript state
Client world: app JavaScript state
Adoption path: run evaluateJavaScript calls in the default client world
Also supported: install WKScriptMessageHandler in a specific content world

关键点:

  • WKContentWorld 隔离的是 JavaScript 全局对象,页面脚本看不到 App 脚本设置的状态。
  • App 脚本仍然能调用 DOM API,也能修改 DOM。
  • evaluateJavaScript 可以指定默认 client world,避免和页面自己的符号冲突。
  • WKScriptMessageHandler 也能安装到指定 content world,把消息入口一起隔离。

3. 少拼字符串,直接调用异步 JavaScript

11:43)旧的 evaluateJavaScript 常见写法是把参数拼进一段新的 JavaScript 字符串。参数来自原生字典或数字时,还要手动序列化。callAsyncJavaScript 允许复用一段固定 JavaScript,并用命名参数传值。

API: callAsyncJavaScript
Input style: constant JavaScript string plus named arguments
Automatic bridge: Cocoa string -> JavaScript string
Automatic bridge: number -> JavaScript number
Automatic bridge: Cocoa dictionary -> JavaScript object
Return rule: JavaScript must explicitly return a value
Promise rule: if the return value is a Promise, completion waits until it resolves

关键点:

  • JavaScript 字符串不再需要为每组参数重新拼接。
  • 参数类型由 API 负责序列化和反序列化,减少数字、字符串、字典之间的手工转换。
  • completion handler 只有在显式 return 后才拿到有意义的值。
  • 返回 Promise 时,completion handler 会等待 promise fulfillment,这让 fetch 这类异步 Web API 更容易接到原生代码。

4. 让 postMessage 自带回复通道

14:18WKScriptMessageHandler 原本更像单向通道。JavaScript 发送消息后,原生侧如果要回复,就要自己维护 message id、回调表和竞态处理。iOS 14 和 macOS Big Sur 中,JavaScript 的 postMessage 返回 Promise,新的 handler 形式会收到 completion handler。

JavaScript side: postMessage returns a Promise
Native side: WKScriptMessageHandler receives a completion handler
Reply path: native code calls the completion handler
JavaScript result: the Promise returned by postMessage resolves

关键点:

  • message reply 不再需要开发者手写一套 id 和回调追踪机制。
  • 原生侧调用 completion handler,就是 JavaScript 侧 promise resolve 的时机。
  • 这适合页面事件要等待 App 侧判断结果的场景,例如评论通知、权限检查、内容选择。

5. 渲染、查找和导出走 WKWebView API

16:56)Browser Pets 想把旧网页放大到现代 iPhone 屏幕。注入 CSS zoom 可以做到,但会和页面样式抢控制权,还可能在 navigation 中产生竞态。WKWebView.pageZoom 把整页缩放变成 WebView 自身属性。

18:39)另一个例子是隐藏网页原来的 header 和 footer。Mac WebView 以前能把自己呈现为某个 media type,WKWebView 在 2020 年把这个能力带到所有平台。App 设置自定义 media type 后,网页已有的 CSS media query 就能切换到 App 专用展示。

19:43)内容层能力也变多了。WKWebView 提供平台一致的页面查找,可配置方向、大小写敏感和 wrapping;找到结果后会选中并滚动到视图中。导出方面,createPDF 可以把屏幕外的完整内容保存成可搜索 PDF,createWebArchiveData 可以保存当前 DOM 和子资源,方便之后调试和测试。

Rendering: WKWebView.pageZoom
Rendering: custom media type on WKWebView
Find: built-in WKWebView find functionality
Export: createPDF with default or custom configuration
Export: createWebArchiveData for DOM plus sub-resources
Load archive: load(data, mimeType)
Mac addition: WKWebView printing on macOS Big Sur

关键点:

  • pageZoom 解决的是整页缩放问题,不需要在每次 navigation 后注入 CSS。
  • 自定义 media type 让网页用 CSS 自己适配 App 容器,适合已有站点保留多套 presentation。
  • 内置 find 功能沿用 AppKit 和 UIKit 的查找习惯,适合长文章、评论、文档页面。
  • createPDF 面向分享和归档,能包含当前屏幕外的内容。
  • createWebArchiveData 面向调试、测试和粘贴板场景,保存的是 Web 内容快照。

6. 默认启用 ITP,并用 App-bound domains 限制深度交互

25:15)Intelligent Tracking Prevention(智能防跟踪,ITP)从 iOS 14 和 macOS Big Sur 开始默认作用于所有 WKWebView App。对于通用浏览器类 App,用户仍然能像 Safari 一样关闭它来处理兼容性问题。

26:46)App-bound domains 用来描述哪些域名是 App 核心实现的一部分。Browser Pets 只把 WebKittens 和 Pups on Safari 放进白名单,其他评论链接仍然能加载,但 App 和引入的框架无法对这些站点做深度交互。这样做符合 least privilege 原则。

Default privacy: ITP enabled for all WKWebView apps
Configuration key: WKAppBoundDomains in Info.plist
Value: array of domains that are core to the app
Outside domains: loading still works
Outside domains: deep interaction is blocked
Strict mode: add WKAppBoundDomains with an empty array

关键点:

  • ITP 是默认隐私保护,不需要 App 主动打开。
  • WKAppBoundDomains 写在 Info.plist,表达 App 信任并深度交互的站点集合。
  • 白名单外的页面仍然可以打开,但不能被 App 深度读取或操控。
  • 如果 App 加载任意网页但不需要和网页内容交互,可以用空数组关闭所有域名的深度交互。

核心启发

  • 做一个安全的多站点信息流:把几个自有网站内容放进同一个原生 feed。为什么值得做:WKContentWorldallowsContentJavaScript 和 App-bound domains 能把 App 脚本、页面脚本、白名单外链接分开管理。怎么开始:先为核心站点配置 WKAppBoundDomains,再把 App 注入脚本迁到默认 client world。

  • 做一个内置网页问题复现包:给测试版 App 加一个“导出当前 Web 状态”入口。为什么值得做:createWebArchiveData 能保存当前 DOM 和子资源,比截图更适合复现页面结构问题。怎么开始:在调试菜单调用 WebArchive 导出,把数据保存到磁盘或加载到另一个 WKWebView 做对照。

  • 做一个原生文档搜索和分享体验:在帮助中心、协议页或评论长页里加入查找和 PDF 分享。为什么值得做:WKWebView 的 find 功能会选中并滚动到结果,createPDF 能导出屏幕外的完整内容。怎么开始:给原生导航栏放搜索按钮和分享按钮,分别触发 find 流程与 PDF 生成流程。

  • 做一个低脚本的内容阅读容器:对资讯、公告、文章页关闭远端页面脚本,只保留 App 自己的脚本能力。为什么值得做:allowsContentJavaScript 可以移除页面自定义滚动、广告脚本或不受控交互,同时不切断 App 的必要控制。怎么开始:在 navigation policy delegate 里按 URL 设置 WKWebPagePreferences

  • 做一个旧站点的 App 专用展示层:让已有网页在 App 内隐藏网页 header/footer,并放大到合适尺寸。为什么值得做:pageZoom 和自定义 media type 能减少注入 CSS 的竞态和副作用。怎么开始:先用 Web Inspector 找出页面已有 media query 或 CSS class,再把缩放和 media type 放到 WKWebView 配置层。

关联 Session

评论

GitHub Issues · utterances