Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WebKit::WebView Essentials / Sapporo.swift #1

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

WebKit::WebView Essentials / Sapporo.swift #1

Avatar for Yutaro Muta

Yutaro Muta

July 25, 2026

More Decks by Yutaro Muta

Other Decks in Technology

Transcript

  1. yutailang0119 • 株式会社はてな ◦ ◦ ◦ • • • @京都オフィス

    アプリケーションエンジニア サービスプラットフォーム try! Swift Tokyo Organizer AVP座談会🥽 🐈󰴛 2
  2. Agenda • Meet / Dive into / Enhance WebKit::WebView •

    Meet / Enhance WebViewBackport ◦ @WebViewBackport • Result 5
  3. Meet WebKit::WebView • SwiftUI::View実装のWebView ◦ iOS / iPadOS / Mac

    Catalyst / macOS / visionOS 26.0+ • 制御状態はWebPageが担当 ◦ @Observable • Meet WebKit for SwiftUI ◦ WWDC25 Session 231 7
  4. Meet WebKit::WebView @available(iOS 26.0, *) struct ContentWebView: View { @State

    private var webPage = WebPage() var body: some View { WebView(webPage) .onAppear { webPage.load(URL(string: "https://apple.com")) } } } 8
  5. Dive into WebKit::WebView • Representableを介し、WKWebViewを表示 ◦ class WebPageWebView: WKWebView •

    値型で再設計された設定 ◦ WebPage.Configuration <- WKWebViewConfiguration • 抽象化したWKWebView関連Delegateを提供 ◦ WebPage.DialogPresenting <- WKUIDelegateAdapter ◦ WebPage.NavigationDeciding <- WKNavigationDelegateAdapter 10
  6. Dive into WebKit::WebView • WebKitに実装されている • Open Source Software ◦

    https://github.com/WebKit/WebKit • SwiftUI APIはSwift overlayに配置 ◦ Source/WebKit/UIProcess/API/Swift/ ▪ UIProcess = アプリ本体のプロセス ▪ 公開APIとUIが置かれる 11
  7. Enhance WebKit::WebView • WebKitを参考にバックポートできそう • 大まかに3つを実装できれば達成可能 ◦ WebViewBackport: SwiftUI::View ◦

    WebViewRepresentableBackport: UIViewRepresentable ◦ @Observable class WebPageBackport • ⚠ 回避必要な箇所がある 13
  8. Enhance WebKit::WebView • Appleのoverlayは非公開APIに依存 ◦ internal import WebKit_Internal ◦ import

    WebKit_Private ◦ @_spi(...) import SwiftUI • third-partyのバックポートは公開APIだけで 再構築する 14
  9. Enhance WebKit::WebView • ① 公開APIで代替可能 -> 置換・ラップ • ② 内部処理を観測

    -> 近似 • ③ 等価なし-> 挙動差・縮小 ◦ SPI、version 15
  10. ① 公開APIで代替可能な置換、ラップ • Delegateをasyncプロトコルに変換 ◦ WKNavigationDelegate -> NavigationDeciding ◦ WKUIDelegate

    -> DialogPresenting • WK* をラップ ◦ FrameInfo・NavigationAction・BackForwardList • NavigationEvent -> some AsyncSequence 17
  11. ② 内部処理を観測して近似 • @ObservableをKVOから再現 ◦ title / url はWKWebView由来のcomputed ◦

    読取: getter で access(keyPath:) 登録 ◦ 変更: KVO(.prior) の前/後 -> willSet/didSet ◦ .prior = 変更前通知(willSet 相当) 19
  12. ③ 等価なし -> 挙動差 • NavigationEvent.finishedのタイミング差 ◦ WebKit: 描画反映を待つ(SPI) ▪

    _do(afterNextPresentationUpdate:) ◦ Backport: didFinish から同期 ◦ 期待挙動と相違 ▪ 完了時にまだ未描画のことがある 21
  13. ③ 等価なし -> 縮小 • バージョンで欠ける(下位OSに無い) ◦ 提供できない / 上位限定

    • バージョンで変わる(型がOSで違う) ◦ 統一シグネチャで包めない 23
  14. Meet WebViewBackport struct ContentWebViewBackport: View { @State private var webPage

    = WebPageBackport() var body: some View { WebViewBackport(webPage) .onAppear { webPage.load(URL(string: "https://apple.com")) } } } 27
  15. Enhance WebViewBackport @WebViewBackport @available(iOS 26.0, *) struct ContentWebView: View {

    @State private var webPage = WebPage() var body: some View { WebView(webPage) .onAppear { webPage.load(URL(string: "https://apple.com")) } } } 30
  16. Enhance WebViewBackport @WebViewBackport @available(iOS 26.0, *) struct ContentWebView: View {

    @State private var webPage = WebPage() var body: some View { WebView(webPage) .onAppear { webPage.load(URL(string: "https://apple.com")) } } } 31
  17. @WebViewBackport • macro WebViewBackport ◦ @attached(peer, names: suffixed(Backport)) ◦ これ自身はWebViewバックポートの実装ではない

    • マクロを付与したスコープ内の特定文字列を 置換した実装を生成 ◦ WebView -> WebViewBackport ◦ WebPage -> WebPageBackport 32
  18. @WebViewBackport struct ContentWebView: View { ... } ↓Expand Macro struct

    ContentWebViewBackport: View { @State private var webPage = WebPageBackport() var body: some View { WebViewBackport(webPage) .onAppear { webPage.load(URL(string: "https://apple.com")) } } 33
  19. @WebViewBackport struct ContentView: View { var body: some View {

    if #available(iOS 26.0, *) { ContentWebView() } else { ContentWebViewBackport() } } } 34
  20. Fix WebKit, but… • WebKitにバグ報告、修正可能 ◦ WebKit Bugzilla • Bug

    311485 ▪ <input type="file"> にファイル選択が反応しない • ⚠ OSにバンドルされるまで直らない ◦ OSバックポートされない 36
  21. 37

  22. Pitfall of @WebViewBackport @WebViewBackport @available(iOS 26.0, *) struct ContentWebView: View

    { @State private var configuration = WebPage.Configuration() @State private var webPage = WebPage() var body: some View { WebView(webPage) .task { let cookies: [HTTPCookie] = ... await configuration.websiteDataStore .httpCookieStore.setCookies(cookies) } 39
  23. Pitfall of @WebViewBackport struct ContentWebView: View { ... } ↓Expand

    Macro struct ContentWebViewBackport: View { @State private var configuration = WebPageBackport.Configuration() @State private var webPage = WebPageBackport() var body: some View { WebViewBackport(webPage) .task { let cookies: [HTTPCookie] = ... await configuration.websiteDataStore .httpCookieStore.setCookies(cookies) } 40
  24. Pitfall of @WebViewBackport struct ContentWebView: View { ... } 💥

    struct ContentWebViewBackport: View { @State private var configuration = WebPageBackport.Configuration() unrecognized selector @State private var webPage = WebPageBackport() -[WKHTTPCookieStore var body: some View { WebViewBackport(webPage) setCookies:completionHandler:] .task { let cookies: [HTTPCookie] = ... await configuration.websiteDataStore .httpCookieStore.setCookies(cookies) } 41
  25. Pitfall of @WebViewBackport • WKHTTPCookieStore.setCookies(_:) ▪ setCookies(_:completionHandler:) ◦ iOS /

    iPadOS / Mac Catalyst / macOS / visionOS 26.0+ • マクロは呼び出し先を検証しない ◦ 実行時エラーが起きうる • iOS 18以下では unrecognized selector 42
  26. Pitfall of @WebViewBackport @WebViewBackport @available(iOS 26.0, *) struct ContentWebView: View

    { var body: some View { WebView(webPage) .task { let cookies: [HTTPCookie] = ... let store = configuration.websiteDataStore.httpCookieStore for cookie in cookies { await store.setCookie(cookie) } } 43
  27. References • WebKit/WebKit ◦ • iOS 18向けバックポートから学ぶWebKit::WebViewの歩き方 ◦ • https://github.com/WebKit/WebKit

    https://fortee.jp/iosdc-japan-2026/proposal/62b52f01-f999-418b-ab65-a561b49f5202 WKHTTPCookieStore.setCookies(_:) async ◦ https://developer.apple.com/documentation/webkit/wkhttpcookiestore/setcookies(_:completio nhandler:) • WebKit Bugzilla Bug 311485 ◦ • https://bugs.webkit.org/show_bug.cgi?id=311485 SwiftUIにおけるWebViewの実装 - cockscomblog? ◦ https://cockscomb.hatenablog.com/entry/2025/06/04/093418 47