Highlight
WKWebView 新增免 JavaScript 的内容操作 API、自动 HTTPS 升级、WebRTC 权限控制和下载管理,让 App 内嵌网页的体验更接近原生浏览器。
核心内容
在 App 里展示网页内容,开发者面临一个选择:用 SFSafariViewController 还是 WKWebView?前者简单但交互受限,后者灵活但需要写大量 JavaScript 注入代码。
WWDC2021 给 WKWebView 补了一批原生 API,很多以前必须注入 JavaScript 才能做的事,现在直接调用 Objective-C/Swift 方法就行。这降低了 native-web 桥接的复杂度,也让 App-Bound Domains 等安全特性可以正常工作。
同时,一批原本只在 Safari 里可用的浏览器级功能也开放了:自动 HTTP 升级 HTTPS、WebRTC 的 getUserMedia、文件下载。这些让 App 内嵌网页的体验更接近完整浏览器。
详细内容
免 JavaScript 操作网页内容
注入 JavaScript 有几个问题:native 和 web 的接口容易写乱、可能产生副作用、而且和 App-Bound Domains 安全特性不兼容。今年新增了三个不需要 JavaScript 的 API。
Theme Color
网页通过 <meta name="theme-color"> 设置主题色,App 可以直接读取:
// 读取网页主题色
if let themeColor = webView.themeColor {
headerView.backgroundColor = themeColor
}
// 读取计算出的背景色(themeColor 未设置时可用)
if let bgColor = webView.underPageBackgroundColor {
scrollView.backgroundColor = bgColor
}
关键点:
themeColor返回网页 meta 标签设置的主题色(05:51)underPageBackgroundColor是计算出的背景色,用于滚动到内容边缘时的填充色(06:12)- Safari 用这两个属性实现网页和浏览器 chrome 的视觉融合(06:22)
- 可以写入
underPageBackgroundColor自定义滚动边缘颜色(06:28)
禁用文本交互
视频内容里,用户可能误触文本选择控件而不是播放按钮:
let preferences = WKPreferences()
preferences.textInteractionEnabled = false
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
let webView = WKWebView(frame: .zero, configuration: configuration)
关键点:
textInteractionEnabled = false关闭所有文本交互(07:01)- 文本选择、放大镜、复制菜单都不会出现
- 适合以媒体播放为主的网页场景
媒体播放控制
以前暂停媒体需要注入 JavaScript 遍历 DOM 找 video/audio 元素。现在直接调 API:
// 暂停页面上所有媒体(等效于对每个 media 元素调用 pause())
webView.pauseAllMediaPlayback()
// 挂起所有媒体播放,新加载的媒体也不会自动播放
webView.setAllMediaPlaybackSuspended(true)
// 恢复媒体播放
webView.setAllMediaPlaybackSuspended(false)
// 关闭所有画中画窗口
webView.closeAllMediaPresentations()
// 查询媒体播放状态
webView.requestMediaPlaybackState { state in
switch state {
case .playing, .paused, .suspended:
print(state)
@unknown default:
break
}
}
关键点:
pauseAllMediaPlayback()只影响当前页面的媒体,刷新后新媒体仍会播放(09:37)setAllMediaPlaybackSuspended(true)是 WebView 级别的设置,刷新后仍然生效(10:08)- 后者还会禁用用户媒体控制,直到恢复(07:57)
- 不需要了解网页 DOM 结构(09:14)
浏览器级功能
自动 HTTPS 升级
iOS 15 和 macOS Monterey 开始,已知支持 HTTPS 的网站的 HTTP 请求会自动升级到 HTTPS(12:04)。不需要开发者做任何事。
如需关闭(比如本地调试):
let configuration = WKWebViewConfiguration()
configuration.upgradeKnownHostsToHTTPS = false // 不建议用于生产环境
关键点:
- 默认开启,无需配置(12:17)
- 只对已知支持 HTTPS 的网站生效(12:07)
- 关闭 flag 不应出现在生产代码中(12:31)
WebRTC / getUserMedia
iOS 14.3 开始 WKWebView 支持 getUserMedia,今年进一步完善:
// 自定义 scheme handler 加载时,权限提示显示 App 名称而非网站 URL
let schemeHandler = MyCustomSchemeHandler()
configuration.setURLSchemeHandler(schemeHandler, forURLScheme: "myapp")
// 控制摄像头和麦克风权限提示
class MyUIDelegate: NSObject, WKUIDelegate {
func webView(
_ webView: WKWebView,
requestMediaCapturePermissionFor origin: WKSecurityOrigin,
initiatedByFrame frame: WKFrameInfo,
type: WKMediaCaptureType,
decisionHandler: @escaping (WKPermissionDecision) -> Void
) {
// 对信任的域名跳过二次提示
if origin.host == "trusted.example.com" {
decisionHandler(.grant)
} else {
decisionHandler(.prompt) // 显示系统提示
}
}
}
webView.uiDelegate = MyUIDelegate()
关键点:
- 自定义 scheme handler 加载时,权限提示显示 App 名称而非网站 URL(12:54)
WKUIDelegate新方法控制何时提示摄像头/麦克风权限(13:15)- 返回
.grant直接授权,.prompt显示提示,.deny拒绝(14:01) - 不实现 delegate 时保持默认行为(系统提示)(13:55)
控制媒体捕获状态
// 关闭摄像头
webView.setCameraCaptureState(.none)
// 开启摄像头
webView.setCameraCaptureState(.active)
// 关闭麦克风
webView.setMicrophoneCaptureState(.none)
// 开启麦克风
webView.setMicrophoneCaptureState(.active)
关键点:
- 直接控制摄像头和麦克风捕获状态,无需 JavaScript(15:04)
- 关闭后状态栏的录制指示器会消失(16:54)
- 开启后指示器重新出现(17:02)
下载管理
WKWebView 新增下载支持,三种触发方式:
网页 JavaScript 触发
// 网页中的 JavaScript
const link = document.createElement('a');
link.href = 'https://example.com/image.jpg';
link.download = 'cute-kitten.jpg';
link.click();
// App 端处理
func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
if navigationAction.shouldPerformDownload {
decisionHandler(.download)
return
}
decisionHandler(.allow)
}
服务器通过 Content-Disposition 触发
func webView(
_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void
) {
let headers = navigationResponse.response as? HTTPURLResponse
if let disposition = headers?.value(forHTTPHeaderField: "Content-Disposition"),
disposition.contains("attachment") {
decisionHandler(.download)
return
}
decisionHandler(.allow)
}
App 主动发起
let request = URLRequest(url: URL(string: "https://example.com/file.pdf")!)
webView.startDownload(using: request) { download in
download.delegate = self
}
下载委托
extension ViewController: WKDownloadDelegate {
func download(
_ download: WKDownload,
decideDestinationUsing response: URLResponse,
suggestedFilename: String,
completionHandler: @escaping (URL?) -> Void
) {
let url = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
.appendingPathComponent(suggestedFilename)
completionHandler(url)
}
func download(
_ download: WKDownload,
didFailWithError error: Error,
resumeData: Data?
) {
// resumeData 可用于恢复下载
if let resumeData = resumeData {
// 保存 resumeData,稍后恢复
}
}
}
关键点:
- 三种触发方式:网页 JS、服务器 header、App 主动调用(17:54)
WKDownload必须设置 delegate,否则下载自动取消(19:09)decideDestinationUsing决定文件保存位置(19:13)- 失败时
resumeData可用于断点续传(19:21)
核心启发
- 优先用原生 API 替代 JavaScript 注入。
pauseAllMediaPlayback、themeColor等 API 让代码更简洁,也避免 App-Bound Domains 被禁用。 - 用
setAllMediaPlaybackSuspended做持久化媒体控制。比pauseAllMediaPlayback更彻底,刷新页面后仍然生效,适合需要全局静音的场景。 - WebRTC 权限委托提升用户体验。对信任的自有域名直接授权,避免用户被连续弹窗打扰;对其他域名保持提示,兼顾安全和体验。
- 下载管理让内容分享变简单。以前 WKWebView 不支持下载,用户只能截屏或长按图片保存。现在可以完整支持文件下载、断点续传。
- 自动 HTTPS 升级减少配置负担。不需要在 App 层做协议升级逻辑,WebKit 自动处理已知支持 HTTPS 的网站。
关联 Session
- Develop advanced web content — Safari 和 WebKit 的新 JavaScript/WebAssembly 特性
- Discover Web Inspector improvements — Web Inspector 调试工具
- Design for Safari 15 — Safari 15 设计适配指南
评论
GitHub Issues · utterances