Slide 1

Slide 1 text

Kyome 2023/03/24 (ۚ) @LINE FukuokaΦϑΟε ϒϥ΢βΞϓϦΛࣗ࡞ͯ͠Θ͔ͬͨ
 WebViewͷѻ͍ํ J04.FFUVQ JO෱Ԭ

Slide 2

Slide 2 text

ࣗݾ঺հ IUUQTLZPNFJP ,ZPNF ,ZPNFTVLF CybozuͰkintoneϞόΠϧͷiOSΛ୲౰ ۀ຿ޮ཰Խ΍OSSϥΠϒϥϦͷ։ൃӡ༻ʹੵۃత झຯͰmacOS޲͚ͷϢʔςΟϦςΟΞϓϦ։ൃ

Slide 3

Slide 3 text

ࣗݾ঺հ RunCat activity indicator HandyPalette color picker ScreenPointer presentation tool QuickMIDI MIDI keyboard player

Slide 4

Slide 4 text

ͳͥϒϥ΢βΞϓϦΛ 
 ࣗ࡞͠Α͏ͱࢥ͔ͬͨ

Slide 5

Slide 5 text

ͳͥϒϥ΢βΞϓϦΛࣗ࡞͠Α͏ͱࢥ͔ͬͨ kintoneʹ͸ʮJavaScriptΧελϚΠζʯͱ͍͏ϢʔβʔಠࣗͷػೳΛ
 ௥ՃͰ͖Δ࢓૊Έ͕͋Δ kintoneϞόΠϧ͸WebViewϕʔεͷΞϓϦͱͳ͍ͬͯΔ WebViewϕʔεͷΞϓϦ։ൃΛ୲౰͢Δʹ͋ͨͬͯɺWebViewΛѻͬͨ
 ͜ͱ͕ͳ͔ͬͨͨΊɺϒϥ΢βΞϓϦΛؙ͝ͱࣗ࡞͢Δ͜ͱʹͨ͠ MinBrowser https://github.com/kyome22/MinBrowserɹ AppStoreͰ഑৴த

Slide 6

Slide 6 text

ͳͥϒϥ΢βΞϓϦΛࣗ࡞͠Α͏ͱࢥ͔ͬͨ ϒϥ΢βΞϓϦΛؙ͝ͱࣗ࡞ֶͯ͠Μͩ͜ͱ WebView಺ͰͷΠϕϯτΛϑοΫͯ͠ωΠςΟϒ΁ಈ࡞Λͭͳ͛Δํ๏ WebView্Ͱ࣮ߦ͞ΕͨJavaScriptͷϩάΛरͬͯग़ྗ͢Δํ๏ WebViewΛབྷΊͨςετέʔεΛ҆ఆ࣮ͯ͠ߦ͢Δํ๏ etc. ࠓճͷ࿩୊ͷൣғ֎

Slide 7

Slide 7 text

SwiftUIͰWKWebViewΛѻ͏

Slide 8

Slide 8 text

UIViewRepresentableͰWKWebViewΛϥοϓͯ͠Viewͱͯ͠࢖͏ ݕࡧɺ໭ΔɺਐΉɺPull to refreshͳͲ͕ՄೳͳΠϯλϥΫςΟϒͳ
 WebViewΛ࡞Γ͍ͨ UIViewRepresentableͷ֎ͷViewͱͷ࿈ܞ͕ඞཁ SwiftUIͰWKWebViewΛѻ͏ UIViewRepresentable WKWebView SearchBar ProgressView ϫʔυݕࡧ ϓϩάϨεͷߋ৽

Slide 9

Slide 9 text

GitHubΛړ͍ͬͯΔͱΑ͘ݟΔख๏ enumͷϓϩύςΟΛ؂ࢹͯ͠WKWebViewͷΞΫγϣϯΛ੍ޚ͢Δ UIViewRepresentableͷCoordinatorͰWKWebViewͷঢ়ଶΛ؂ࢹ͢Δ SwiftUIͰWKWebViewΛѻ͏

Slide 10

Slide 10 text

GitHubΛړ͍ͬͯΔͱΑ͘ݟΔख๏ enumͷϓϩύςΟΛ؂ࢹͯ͠WKWebViewͷΞΫγϣϯΛ੍ޚ͢Δ UIViewRepresentableͷCoordinatorͰWKWebViewͷঢ়ଶΛ؂ࢹ͢Δ SwiftUIͰWKWebViewΛѻ͏ struct WrappedWKWebView: UIViewRepresentable { enum Action { case none case search(String) } @Binding private var action: Action ϫʔυݕࡧ༻ͷaction

Slide 11

Slide 11 text

GitHubΛړ͍ͬͯΔͱΑ͘ݟΔख๏ enumͷϓϩύςΟΛ؂ࢹͯ͠WKWebViewͷΞΫγϣϯΛ੍ޚ͢Δ UIViewRepresentableͷCoordinatorͰWKWebViewͷঢ়ଶΛ؂ࢹ͢Δ SwiftUIͰWKWebViewΛѻ͏ func updateUIView(_ webView: WKWebView, context: Context) { switch action { case .none: break case .search(let searchText): context.coordinator.search(text: searchText) action = .none } } action͕searchʹͳͬͨΒcoordinatorΛհͯ͠
 WKWebViewͷload(request:)Λୟ͘ actionΛ࢖͍ऴΘͬͨΒ.noneʹ໭͢

Slide 12

Slide 12 text

GitHubΛړ͍ͬͯΔͱΑ͘ݟΔख๏ enumͷϓϩύςΟΛ؂ࢹͯ͠WKWebViewͷΞΫγϣϯΛ੍ޚ͢Δ UIViewRepresentableͷCoordinatorͰWKWebViewͷঢ়ଶΛ؂ࢹ͢Δ SwiftUIͰWKWebViewΛѻ͏ struct WrappedWKWebView: UIViewRepresentable { @Binding private var progress: Double private let webView = WKWebView() func makeCoordinator() -> Coordinator { return Coordinator(webView: webView, progress: $progress) } coordinatorʹwebViewͱBinding͍ͨ͠ϓϩύςΟΛ౉͢

Slide 13

Slide 13 text

GitHubΛړ͍ͬͯΔͱΑ͘ݟΔख๏ enumͷϓϩύςΟΛ؂ࢹͯ͠WKWebViewͷΞΫγϣϯΛ੍ޚ͢Δ UIViewRepresentableͷCoordinatorͰWKWebViewͷঢ়ଶΛ؂ࢹ͢Δ SwiftUIͰWKWebViewΛѻ͏ class Coordinator { init(webView: WKWebView, progress: Binding) { // ॳظԽ͸লུ self.webView 
 .publisher(for: \.estimatedProgress) .sink { [weak self] value in self?.progress = value } .store(in: &cancellables) } } WKWebViewͷঢ়ଶΛ؂ࢹͯ͠Bindingʹྲྀ͢

Slide 14

Slide 14 text

SwiftUIͰWKWebViewΛѻ͏ ໰୊ൃੜ ϫʔυݕࡧͨ͠ޙϩʔυॲཧ͕ऴΘΒͣɺ
 Կ౓΋ϩʔυ͞ΕΔΑ͏ͳڍಈʹͳͬͨ ͔͠΋XcodeͰ͸େྔͷܯࠂ͕ʂ 🤯 ѹ౗తڍಈෆ৹

Slide 15

Slide 15 text

⚠ ໰୊఺ ܯࠂʹ͋Δ௨ΓɺViewͷߋ৽தʹBinding͍ͯ͠Δ஋ͷߋ৽Λ͍ͯ͠Δ͜ͱ WebView͕ViewͰ͋Δͷʹঢ়ଶΛ͓࣋ͬͯΓɺ
 View֎෦͔Βঢ়ଶΛมߋͨ͠Γ؂ࢹͨ͠Γ͢Δඞཁ͕͋Δ͜ͱ͕ݪҼ WebView͸એݴతUIͱͷ૬ੑ͕ѱ͘ɺೃછΈͮΒ͍ SwiftUIͰWKWebViewΛѻ͏

Slide 16

Slide 16 text

💡ղܾࡦ UIViewRepresentableͷ֎ʹWKWebViewͷΠϯελϯεΛ࣋ͨͤɺ
 ͦͪΒͰঢ়ଶͷ؂ࢹͱ੍ޚΛߦ͏ SwiftUIͰWKWebViewΛѻ͏ UIViewRepresentable WKWebView SearchBar ProgressView ϫʔυݕࡧ ϓϩάϨεͷߋ৽ WebViewModel ϨϯμϦϯάػೳ͚ͩ WKWebView ঢ়ଶͷ؂ࢹͱ੍ޚ

Slide 17

Slide 17 text

💡ղܾࡦ UIViewRepresentableͷ֎ʹWKWebViewͷΠϯελϯεΛ࣋ͨͤɺ
 ͦͪΒͰঢ়ଶͷ؂ࢹͱ੍ޚΛߦ͏ SwiftUIͰWKWebViewΛѻ͏ struct WrappedWKWebView: UIViewRepresentable { let setWebViewHandler: (WKWebView) -> Void func makeUIView(context: Context) -> some WKWebView { let webView = WKWebView() setWebViewHandler(webView) return webView } func updateUIView(_ uiView: UIViewType, context: Context) {} } WKWebViewΛ֎ʹ࣋ͪग़͢΍ͭ Կ΋͠ͳ͍

Slide 18

Slide 18 text

💡ղܾࡦ UIViewRepresentableͷ֎ʹWKWebViewͷΠϯελϯεΛ࣋ͨͤɺ
 ͦͪΒͰঢ়ଶͷ؂ࢹͱ੍ޚΛߦ͏ SwiftUIͰWKWebViewΛѻ͏ class WebViewModel: ObservableObject { private weak var webView: WKWebView? func setWebView(_ webView: WKWebView) { self.webView = webView // webViewͷঢ়ଶΛ؂ࢹ(ߪಡ)ͯ͠PublishedͳϓϩύςΟʹྲྀ͢ } func search() { // URLRequestΛ࡞ͬͯ webView?.load() Λୟ͘ } }

Slide 19

Slide 19 text

💡ղܾࡦ UIViewRepresentableͷ֎ʹWKWebViewͷΠϯελϯεΛ࣋ͨͤɺ
 ͦͪΒͰঢ়ଶͷ؂ࢹͱ੍ޚΛߦ͏ SwiftUIͰWKWebViewΛѻ͏ struct WebView: View { @StateObject private var webViewModel = WebViewModel() var body: some View { VStack(spacing: 0) { SearchBar(inputText: $webViewModel.inputText) .onSubmit { webViewModel.search() } ProgressView(value: webViewModel.progress) WrappedWKWebView(setWebViewHandler: { webView in webViewModel.setWebView(webView) }) } } } Modelͷsearch()Λୟ͘ WKWebViewͷΠϯελϯεΛModelʹ౉͢

Slide 20

Slide 20 text

💡ղܾࡦ UIViewRepresentableͷ֎ʹWKWebViewͷΠϯελϯεΛ࣋ͨͤɺ
 ͦͪΒͰঢ়ଶͷ؂ࢹͱ੍ޚΛߦ͏ SwiftUIͰWKWebViewΛѻ͏ struct WebView: View { @StateObject private var webViewModel = WebViewModel() var body: some View { VStack(spacing: 0) { SearchBar(inputText: $webViewModel.inputText) .onSubmit { webViewModel.search() } ProgressView(value: webViewModel.progress) WrappedWKWebView(setWebViewHandler: { webView in webViewModel.setWebView(webView) }) } } } Modelͷsearch()Λୟ͘ WKWebViewͷΠϯελϯεΛModelʹ౉͢ Q, Կ౓΋ݺͼग़͞Εͯ͠·͏ͷͰ͸ʁ A, UIViewRepresentableͷmakeUIView() ͸ 
 Ұ౓͔͠ݺ͹Εͳ͍ͷͰ໰୊ͳ͍

Slide 21

Slide 21 text

💡ղܾࡦ UIViewRepresentableͷ֎ʹWKWebViewͷΠϯελϯεΛ࣋ͨͤɺ
 ͦͪΒͰঢ়ଶͷ؂ࢹͱ੍ޚΛߦ͏ SwiftUIͰWKWebViewΛѻ͏ struct WebView: View { @StateObject private var webViewModel = WebViewModel() var body: some View { VStack(spacing: 0) { SearchBar(inputText: $webViewModel.inputText) .onSubmit { webViewModel.search() } ProgressView(value: webViewModel.progress) WrappedWKWebView(setWebViewHandler: { webView in webViewModel.setWebView(webView) }) } } } Modelͷsearch()Λୟ͘ WKWebViewͷΠϯελϯεΛModelʹ౉͢ Q, Կ౓΋ݺͼग़͞Εͯ͠·͏ͷͰ͸ʁ A, UIViewRepresentableͷmakeUIView() ͸ 
 Ұ౓͔͠ݺ͹Εͳ͍ͷͰ໰୊ͳ͍

Slide 22

Slide 22 text

Thank you! WKWebViewʹ͍ͭͯ MinBrowserͷϦϦʔεʹۤઓͨ݅͠ ݸਓΞϓϦ։ൃʹ͍ͭͯ ࠙਌ձͰ͓࿩͠͠·͠ΐ͏ʂ

Slide 23

Slide 23 text

খ࿩

Slide 24

Slide 24 text

MinBrowserͷϦϦʔεʹۤઓͨ݅͠

Slide 25

Slide 25 text

ग़དྷ্͕ͬͨͷͰϦϦʔε͠Α͏ͱࢥͬͨΒɺ৹ࠪʹ6ϲ݄΋͔͔ͬͨ😵
 ʢ2022/09/04 ৹ࠪఏग़ɺ2023/03/02 ഑৴։࢝ʣ Webϒϥ΢βͳͷͰʮແ੍ݶͷWebΞΫηεؚ͕·ΕΔʯʹ ☑ ͕ඞཁ ๫ྗతͳදݱɺΪϟϯϒϧɺաܹͳੑత಺༰ͳͲશͯͷ೥ྸ੍ݶ߲໨
 ʹ͍ͭͯʮසൟ/ۃ౓ʯʹ ☑ ͕ඞཁͩͱࢥ͍ࠐΜͰ͍ͨ ݁Ռతʹ৹ࠪʹ௕͔͔࣌ؒΔϧʔτʹೖͬͯ͠·ͬͨ MinBrowserͷϦϦʔεʹۤઓͨ͠࿩

Slide 26

Slide 26 text

৹ࠪʹ௕͔͔࣌ؒΔ৔߹ɺεςʔλε͸RejectʹͳΓ
 ʮ͔͔࣌ؒΔ͚Ͳ٫ԼͰ͸ͳ͍͔Β଴ͬͯͯͶʯͱϝοηʔδ͕ಧ͘ ΞϓϦࣗମ͕೥ྸ੍ݶ߲໨ΛҙਤతʹؚΜͰ͍ͳ͚Ε͹ ☑ ͸ෆཁͩͬͨ ʮແ੍ݶͷWebΞΫηεؚ͕·ΕΔʯ͚ͩʹ ☑ ͨ͠Β൒೔Ͱ৹ࠪ௨ͬͨ MinBrowserͷϦϦʔεʹۤઓͨ͠࿩ ͱ΄΄…