WWDC Quick Look 💓 By SwiftGGTeam
Explore WKWebView additions

Explore WKWebView additions

观看原视频

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)

核心启发

  1. 优先用原生 API 替代 JavaScript 注入pauseAllMediaPlaybackthemeColor 等 API 让代码更简洁,也避免 App-Bound Domains 被禁用。
  2. setAllMediaPlaybackSuspended 做持久化媒体控制。比 pauseAllMediaPlayback 更彻底,刷新页面后仍然生效,适合需要全局静音的场景。
  3. WebRTC 权限委托提升用户体验。对信任的自有域名直接授权,避免用户被连续弹窗打扰;对其他域名保持提示,兼顾安全和体验。
  4. 下载管理让内容分享变简单。以前 WKWebView 不支持下载,用户只能截屏或长按图片保存。现在可以完整支持文件下载、断点续传。
  5. 自动 HTTPS 升级减少配置负担。不需要在 App 层做协议升级逻辑,WebKit 自动处理已知支持 HTTPS 的网站。

关联 Session

评论

GitHub Issues · utterances