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

WebViewの現在地 - SwiftUI時代のWebKit - / The Current...

WebViewの現在地 - SwiftUI時代のWebKit - / The Current State Of WebView

2025/06/26
Ebisu.mobile #10 〜WWDC & Google I/O スペシャル!〜

Avatar for marcy731

marcy731

June 25, 2025
Tweet

More Decks by marcy731

Other Decks in Programming

Transcript

  1. Ebisu.mobile #10 ʙWWDC & Google I/O εϖγϟϧʂʙ WebViewͷݱࡏ஍ - SwiftUI࣌୅ͷWebKit

    - STORESגࣜձࣾ ϞόΠϧ։ൃຊ෦ Ϛωʔδϟʔ/iOSΤϯδχΞ ௕୩઒ ক࢘ʢ@marcy731ʣ
  2. ࣗݾ঺հ Masashi Hasegawa / ௕୩઒ ক࢘ ςΫϊϩδʔ෦໳ / ϞόΠϧ։ൃຊ෦ •

    2022೥4݄ STORESʢچ heyʣגࣜձࣾ ʹೖࣾ ◦ STORES Ϩδ / STORES ϒϥϯυΞϓϦ ▪ Ϛωʔδϟʔ / iOSΤϯδχΞ @marcy731
  3. import WebKit struct WebView: UIViewRepresentable { func makeUIView(...) -> WKWebView

    { ... } ... } • NavigationDelegate ͷ࣮૷͕Viewͱ཭ΕͨΓ😣 • ঢ়ଶ؅ཧʹ೰·͞ΕͨΓ😩 • ਖ਼௚SwiftUIͱͷ૬ੑ͸ඍົ😢
  4. • ֤࣮ࣾ૷Λ޻෉͍ͯ͠Δ • cybozu/WebUI • proxy Λհͯ͠ૢ࡞͢Δ ScrollView Style •

    kylehickinson/SwiftUI-WebView (Brave) • WebViewStore ʹ WKWebView Λอ࣋͠ɺঢ়ଶ؅ཧ • https://github.com/cybozu/WebUI • https://github.com/kylehickinson/SwiftUI-WebView
  5. import WebKit struct ContentView: View { var body: some View

    { WebView( url: URL(string: "https://www.st.inc/") ) } }
  6. WebPage 16 import WebKit struct ContentView: View { var body:

    some View { WebView( url: URL(string: "https://www.st.inc/") ) } }
  7. WebPage 17 import WebKit struct ContentView: View { @State private

    var page = WebPage() var body: some View { WebView(page) } }
  8. WebPage 18 import WebKit struct ContentView: View { @State private

    var page = WebPage() var body: some View { WebView(page) .onAppear { page.load(URLRequest( url: URL(string: "https://www.st.inc")!) )) } } }
  9. WebPage 19 import WebKit struct ContentView: View { @State private

    var page = WebPage() var body: some View { WebView(page) } } • SwiftUI ༻ʹઃܭ͞Εͨ Web ίϯςϯπͷঢ়ଶ؅ཧΫϥε • ಺෦ʹ WKWebView Λอ࣋ • → جຊతʹ͸ WKWebView ͰͰ͖Δ͜ͱ͸શ෦Ͱ͖Δ • λΠτϧɾURLɾಡΈࠐΈਐḿͳͲΛ Observable ʹఏڙ • JavaScript ࣮ߦ΍φϏήʔγϣϯ੍ޚͳͲɺDelegate ॲཧΛ Swifty ʹ౷߹
  10. ঢ়ଶΛ؍࡯͢Δ 22 import WebKit struct ContentView: View { @State private

    var page = WebPage() var body: some View { WebView(page) } }
  11. ঢ়ଶΛ؍࡯͢Δ 23 import WebKit struct ContentView: View { @State private

    var page = WebPage() var body: some View { WebView(page) } } extension WebPage : Observable {}
  12. ঢ়ଶΛ؍࡯͢Δ 24 import WebKit struct ContentView: View { @State private

    var page = WebPage() var body: some View { WebView(page) .navigationTitle(page.title) } } extension WebPage : Observable {}
  13. ঢ়ଶΛ؍࡯͢Δ 25 ϓϩύςΟ໊ ܕ આ໌ url URL? ݱࡏදࣔதͷϖʔδͷ URL title

    String ݱࡏදࣔதͷϖʔδͷλΠτϧʢnil ʹͳΒͳ͍ʣ estimatedProgress Double ϖʔδಡΈࠐΈਐḿʢ0.0ʙ1.0ʣ isLoading Bool ϖʔδ͕ಡΈࠐΈத͔Ͳ͏͔ serverTrust SecTrust? TLS ূ໌ॻͷݕূ৘ใʢηΩϡϦςΟؔ࿈ʣ hasOnlySecureContent Bool ಡΈࠐΜͩશϦιʔε͕ HTTPS ͳͲͷ҆શͳ௨৴͔ isWritingToolsActive Bool Writing ToolsʢࣗಈೖྗͳͲʣ͕༗ޮ͔ isBlockedByScreenTime Bool εΫϦʔϯλΠϜʹΑ੍ͬͯݶ͞Ε͍ͯΔ͔ʢvisionOSͰ͸ແޮʣ fullscreenState WebPage.FullscreenState ϑϧεΫϦʔϯঢ়ଶʢinFullscreen ͳͲʣ cameraCaptureState WKMediaCaptureState Χϝϥར༻த͔Ͳ͏͔ microphoneCaptureState WKMediaCaptureState ϚΠΫར༻த͔Ͳ͏͔ • WebPageͰ؂ࢹͰ͖Δ • جຊతʹ͸WKWebViewͱಉ౳
  14. φϏήʔγϣϯΛ؅ཧ͢Δ 32 let id = page.load(URLRequest(url: url)) let events =

    Observations { page.currentNavigationEvent } for await event in events where event?.navigationID == id { switch event.kind { case .startedProvisionalNavigation: print("🔄 ಡΈࠐΈ։࢝") case .receivedServerRedirect: print("🔄 αʔόʔϦμΠϨΫτ") case .committed: print("📦 ίϯςϯπड৴։࢝") case .finished: print("✅ ಡΈࠐΈ׬ྃ") case let .failed(error): print("❌ Τϥʔ: \(error)") case let .failedProvisionalNavigation(error): print("❌ ॳظͷφϏήʔγϣϯॲཧதʹΤϥʔ: \(error)") } }
  15. φϏήʔγϣϯΛ؅ཧ͢Δ 33 let id = page.load(URLRequest(url: url)) let events =

    Observations { page.currentNavigationEvent } for await event in events where event?.navigationID == id { switch event.kind { case .startedProvisionalNavigation: print("🔄 ಡΈࠐΈ։࢝") case .receivedServerRedirect: print("🔄 αʔόʔϦμΠϨΫτ") case .committed: print("📦 ίϯςϯπड৴։࢝") case .finished: print("✅ ಡΈࠐΈ׬ྃ") case let .failed(error): print("❌ Τϥʔ: \(error)") case let .failedProvisionalNavigation(error): print("❌ ॳظͷφϏήʔγϣϯॲཧதʹΤϥʔ: \(error)") } }
  16. φϏήʔγϣϯΛ؅ཧ͢Δ 34 let id = page.load(URLRequest(url: url)) let events =

    Observations { page.currentNavigationEvent } for await event in events where event?.navigationID == id { switch event.kind { case .startedProvisionalNavigation: print("🔄 ಡΈࠐΈ։࢝") case .receivedServerRedirect: print("🔄 αʔόʔϦμΠϨΫτ") case .committed: print("📦 ίϯςϯπड৴։࢝") case .finished: print("✅ ಡΈࠐΈ׬ྃ") case let .failed(error): print("❌ Τϥʔ: \(error)") case let .failedProvisionalNavigation(error): print("❌ ॳظͷφϏήʔγϣϯॲཧதʹΤϥʔ: \(error)") } } Swift 6.2 Ͱར༻Մೳͳ Observations API Λར༻࣮͠ߦ
  17. φϏήʔγϣϯΛ؅ཧ͢Δ 35 let id = page.load(URLRequest(url: url)) let events =

    Observations { page.currentNavigationEvent } for await event in events where event?.navigationID == id { switch event.kind { case .startedProvisionalNavigation: print("🔄 ಡΈࠐΈ։࢝") case .receivedServerRedirect: print("🔄 αʔόʔϦμΠϨΫτ") case .committed: print("📦 ίϯςϯπड৴։࢝") case .finished: print("✅ ಡΈࠐΈ׬ྃ") case let .failed(error): print("❌ Τϥʔ: \(error)") case let .failedProvisionalNavigation(error): print("❌ ॳظͷφϏήʔγϣϯॲཧதʹΤϥʔ: \(error)") } } for-await Λར༻ͯ͠ φϏήʔγϣϯΠϕϯτΛऔಘ͠ɺ ϋϯυϦϯά
  18. JS࿈ܞ΋Swiftyʹ 37 import WebKit struct ContentView: View { @State private

    var page = WebPage() var body: some View { WebView(page) .onAppear { Task { } } } }
  19. JS࿈ܞ΋Swiftyʹ 38 import WebKit struct ContentView: View { @State private

    var page = WebPage() var body: some View { WebView(page) .onAppear { Task { let result = try await page.callJavaScript(""" return document.title; """) } } } }