Share
Embed
Transcript
48 slides
-
yutailang0119 • 株式会社はてな ◦ ◦ ◦ • • • @京都オフィス
アプリケーションエンジニア サービスプラットフォーム try! Swift Tokyo Organizer AVP座談会🥽 🐈 2 -
Agenda • Meet / Dive into / Enhance WebKit::WebView •
Meet / Enhance WebViewBackport ◦ @WebViewBackport • Result 5 -
Meet WebKit::WebView • SwiftUI::View実装のWebView ◦ iOS / iPadOS / Mac
Catalyst / macOS / visionOS 26.0+ • 制御状態はWebPageが担当 ◦ @Observable • Meet WebKit for SwiftUI ◦ WWDC25 Session 231 7 -
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 -
Dive into WebKit::WebView • Representableを介し、WKWebViewを表示 ◦ class WebPageWebView: WKWebView •
値型で再設計された設定 ◦ WebPage.Configuration <- WKWebViewConfiguration • 抽象化したWKWebView関連Delegateを提供 ◦ WebPage.DialogPresenting <- WKUIDelegateAdapter ◦ WebPage.NavigationDeciding <- WKNavigationDelegateAdapter 10 -
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 -
Enhance WebKit::WebView • WebKitを参考にバックポートできそう • 大まかに3つを実装できれば達成可能 ◦ WebViewBackport: SwiftUI::View ◦
WebViewRepresentableBackport: UIViewRepresentable ◦ @Observable class WebPageBackport • ⚠ 回避必要な箇所がある 13 -
Enhance WebKit::WebView • Appleのoverlayは非公開APIに依存 ◦ internal import WebKit_Internal ◦ import
WebKit_Private ◦ @_spi(...) import SwiftUI • third-partyのバックポートは公開APIだけで 再構築する 14 -
Enhance WebKit::WebView • ① 公開APIで代替可能 -> 置換・ラップ • ② 内部処理を観測
-> 近似 • ③ 等価なし-> 挙動差・縮小 ◦ SPI、version 15 -
① 公開APIで代替可能な置換、ラップ • Delegateをasyncプロトコルに変換 ◦ WKNavigationDelegate -> NavigationDeciding ◦ WKUIDelegate
-> DialogPresenting • WK* をラップ ◦ FrameInfo・NavigationAction・BackForwardList • NavigationEvent -> some AsyncSequence 17 -
② 内部処理を観測して近似 • @ObservableをKVOから再現 ◦ title / url はWKWebView由来のcomputed ◦
読取: getter で access(keyPath:) 登録 ◦ 変更: KVO(.prior) の前/後 -> willSet/didSet ◦ .prior = 変更前通知(willSet 相当) 19 -
③ 等価なし -> 挙動差 • NavigationEvent.finishedのタイミング差 ◦ WebKit: 描画反映を待つ(SPI) ▪
_do(afterNextPresentationUpdate:) ◦ Backport: didFinish から同期 ◦ 期待挙動と相違 ▪ 完了時にまだ未描画のことがある 21 -
③ 等価なし -> 挙動差 • BackForwardListをprivateデリゲートで取得 ◦ 履歴変化を知る公開手段がない ▪ @objc(_webView:backForwardListItemAdded:removed:)
◦ privateデリゲート ◦ 未公開のため保証なし -> 将来壊れうる 22 -
③ 等価なし -> 縮小 • バージョンで欠ける(下位OSに無い) ◦ 提供できない / 上位限定
• バージョンで変わる(型がOSで違う) ◦ 統一シグネチャで包めない 23 -
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 -
Meet WebViewBackport • 使用側のインターフェースは同じ • 設定、Delegateもサポート ◦ WebPageBackport.Configuration ◦ any
WebPageBackport.NavigationDeciding ◦ any WebPageBackport.DialogPresenting 28 -
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 -
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 -
@WebViewBackport • macro WebViewBackport ◦ @attached(peer, names: suffixed(Backport)) ◦ これ自身はWebViewバックポートの実装ではない
• マクロを付与したスコープ内の特定文字列を 置換した実装を生成 ◦ WebView -> WebViewBackport ◦ WebPage -> WebPageBackport 32 -
@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 -
@WebViewBackport struct ContentView: View { var body: some View {
if #available(iOS 26.0, *) { ContentWebView() } else { ContentWebViewBackport() } } } 34 -
Fix WebKit, but… • WebKitにバグ報告、修正可能 ◦ WebKit Bugzilla • Bug
311485 ▪ <input type="file"> にファイル選択が反応しない • ⚠ OSにバンドルされるまで直らない ◦ OSバックポートされない 36 -
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 -
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 -
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 -
Pitfall of @WebViewBackport • WKHTTPCookieStore.setCookies(_:) ▪ setCookies(_:completionHandler:) ◦ iOS /
iPadOS / Mac Catalyst / macOS / visionOS 26.0+ • マクロは呼び出し先を検証しない ◦ 実行時エラーが起きうる • iOS 18以下では unrecognized selector 42 -
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 -
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