iOSDC Japan 2023 day0 18:45~ TrackA で登壇するさいの資料になります。 https://fortee.jp/iosdc-japan-2023/proposal/8a41d745-7906-41ca-b05d-c6551c1d23a8
ヘイ株式会社ステートマシンを活用したWebView-ネイティブ間連携へのアプローチSTORES株式会社iOSエンジニア長谷川 将司(@marcy731)2023/09/01 18:45〜 iOSDC Japan TrackA レギュラートーク(20分)
View Slide
アジェンダ2❏ WebView-ネイティブ間連携の基礎知識❏ WebView-ネイティブ間の認証同期の課題❏ ステートマシンを活用した状態管理❏ 具体的な実装方法
注意事項3❏ 本トークはWebView = WKWebViewとして扱います。❏ 一部文字が小さい箇所もあり、遠くの席からだと見えないかもしれません。本資料はX(旧Twitter)にて共有していますので、手元においてご覧ください。
WebView-ネイティブ間連携の基礎知識0.5
基礎知識5❏ WebViewとネイティブ側を「連携する」とは❏ WebViewとネイティブ側が、効果的に情報を共有し相互に連動 すること❏ ネイティブ <-> WebViewのデータの受け渡し❏ ex: JavaScript経由でデータを送信するなど❏ WebView内でのユーザーアクションをネイティブ側でトリガー❏ ex: ボタン押下❏ ネイティブ側からWebView内のJavaScript関数を呼び出して、特定のアクションを実行
基礎知識6❏ 「連携」させるため、まずは以下をおさらいする❏ WKNavigationDelegate❏ URLへのアクセス許可やURLリクエストの読み込み状況を把握するためのAPI❏ WKScriptMessageHandler❏ Webコンテンツ側からネイティブ側にメッセージングするAPI❏ WKUserScript❏ WebコンテンツにJavaScriptを挿入するAPI❏ evaluateJavaScript(_:completionHandler:)❏ 任意のタイミングでJavaScriptを実行するAPI
WKNavigationDelegate
❏ WKNavigationDelegate❏ URLへのアクセス許可やURLリクエストの読み込み状況を把握するためのAPI❏ つまり連携のタイミングの検知WKNavigationDelegateメソッドの一例8基礎知識 - WKNavigationDelegate -
❏ WKNavigationDelegate❏ URLへのアクセス許可やURLリクエストの読み込み状況を把握するためのAPI❏ つまり連携のタイミングの検知WKNavigationDelegateメソッドの一例9基礎知識 - WKNavigationDelegate -
❏ WKNavigationDelegate❏ URLへのアクセス許可やURLリクエストの読み込み状況を把握するためのAPI❏ つまり連携のタイミングの検知WKNavigationDelegateメソッドの一例10基礎知識 - WKNavigationDelegate -
❏ WebコンテンツがSPA*の場合、❏ URLが変わっても、WKNavigationDelegateは呼ばれません❏ その時は、KVOを利用しよう11* SPA: Single Page Application基礎知識 - KVO: Key-Value Observing -
❏ KVO: Key-Value Observing❏ 対象のオブジェクトを監視し、変更を通知してくれる仕組みKVO実装例12基礎知識 - KVO: Key-Value Observing -urlを監視し、変更があればクロージャーが呼ばれる
❏ KVO: Key-Value Observing❏ 対象のオブジェクトを監視し、変更を通知してくれる仕組みCombineでの実装例13基礎知識 - KVO: Key-Value Observing -urlを監視し、変更があればsinkのクロージャーが呼ばれる
14基礎知識 - KVO: Key-Value Observing -❏ 監視できるWebViewのプロパティ
WKScriptMessageHandler
❏ WKScriptMessageHandler❏ Webコンテンツ側からネイティブ側にメッセージングするAPI❏ keyとなるmessageHandlerNameを決めてWebView->ネイティブにメッセージを送る16基礎知識 - WKScriptMessageHandler -
Webコンテンツ側ではJavaScriptでメッセージを送信17基礎知識 - WKScriptMessageHandler -①あらかじめ決めたkeyに対してpostMessageを発火し、 ネイティブ側にメッセージを送信 この例では ”messageHandlerName” というkeyを設定
ネイティブ側ではJavaScriptからのメッセージを受信18基礎知識 - WKScriptMessageHandler -①あらかじめ決めたkeyを webView.configuratin.userContentControllerに登録
ネイティブ側ではJavaScriptからのメッセージを受信19基礎知識 - WKScriptMessageHandler -②JavaScriptのpostMessageをDelegateメソッド内で受信
ネイティブ側ではJavaScriptからのメッセージを受信20基礎知識 - WKScriptMessageHandler -③受け取ったデータをパース
WKUserScript
❏ WKUserScript❏ WebコンテンツにJavaScriptを挿入するAPI実装例22基礎知識 - WKUserScript -
❏ WKUserScript❏ WebコンテンツにJavaScriptを挿入するAPI実装例23基礎知識 - WKUserScript -①JavaScriptをWKUserScriptに変換②WKUserContentControllerに追加
❏ WKUserScript❏ WebコンテンツにJavaScriptを挿入するAPI実装例24基礎知識 - WKUserScript -③WKWebViewConfigurationにuserContentControllerセット
❏ WKUserScript❏ WebコンテンツにJavaScriptを挿入するAPI実装例25基礎知識 - WKUserScript -④configurationでWKWebViewを初期化
evaluateJavaScript(_:completionHandler:)
27基礎知識 - evaluateJavaScript(_:completionHandler:) -❏ evaluateJavaScript(_:completionHandler:)❏ 任意のタイミングでJavaScriptを実行するAPI実装例
28基礎知識 - evaluateJavaScript(_:completionHandler:) -❏ evaluateJavaScript(_:completionHandler:)❏ 任意のタイミングでJavaScriptを実行するAPI実装例 「読み込み完了時」に実行実行後、クロージャー内が呼ばれるJavaScriptの実行結果を取得できる
WebView-ネイティブ間連携の基礎知識 - まとめ -29❏ WebViewとネイティブ側の連携❏ WKNavigationDelegate❏ WKScriptMessageHandler❏ WKUserScript❏ WKWenView.evaluateJavaScript(_:completionHandler:)❏ これらの方法を駆使し、適切なタイミングで連携していく
WebView-ネイティブ間の認証同期の課題6.5
認証同期の課題31❏ 題材:外部*のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい* 外部: 内部構造に干渉することができないWebアプリケーション
認証同期の課題32❏ 題材:外部*のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい≒WebView内でログインしたらアプリ側でもログイン* 外部: 内部構造に干渉することができないWebアプリケーション
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい33❏ ネイティブ(App)とWebView(EC)の関係他社管理・アカウント管理・ポイント付与・購買履歴分析・お知らせ送付・クーポン送付AppWebView(ECサイト)NativeAppBackendアカウント情報(Webhook)ログイン・会員情報・アカウント管理・在庫管理・注文管理・発送管理ECサイト/Backend
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい34❏ ネイティブ(App)とWebView(EC)の関係他社管理・アカウント管理・ポイント付与・購買履歴分析・お知らせ送付・クーポン送付AppWebView(ECサイト)NativeAppBackendアカウント情報(Webhook)WebView(ECサイト)でログイン→ Appでもログインログイン・会員情報・アカウント管理・在庫管理・注文管理・発送管理ECサイト/Backend
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい35❏ ログインシーケンスApp EC(WebView) App BackendEC Backend①ログインページ表示
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい36❏ ログインシーケンスApp EC(WebView) App BackendEC Backend①ログインページ表示②email/pass入力
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい37❏ ログインシーケンスApp EC(WebView) App Backend①ログインページ表示③ECログインリクエスト②email/pass入力EC Backend
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい38❏ ログインシーケンスApp EC(WebView) App BackendEC Backend①ログインページ表示③ECログインリクエスト②email/pass入力④入力したemailを取得
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい39❏ ログインシーケンスApp EC(WebView) App BackendEC Backend①ログインページ表示③ECログインリクエスト⑤ECログイン成功②email/pass入力④入力したemailを取得
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい40❏ ログインシーケンスApp EC(WebView) App BackendEC Backend①ログインページ表示③ECログインリクエスト⑤ECログイン成功⑥ECログイン成功②email/pass入力④入力したemailを取得
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい41❏ ログインシーケンスApp EC(WebView) App BackendEC Backend①ログインページ表示③ECログインリクエスト⑤ECログイン成功⑥ECログイン成功②email/pass入力④入力したemailを取得⑦AppログインAPI with email
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい42❏ ログインシーケンスApp EC(WebView) App BackendEC Backend①ログインページ表示③ECログインリクエスト⑤ECログイン成功⑥ECログイン成功②email/pass入力④入力したemailを取得⑦AppログインAPI with email⑧Appログイン成功
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい43❏ ログインシーケンスApp EC(WebView) App BackendEC Backend①ログインページ表示③ECログインリクエスト⑤ECログイン成功⑥ECログイン成功②email/pass入力④入力したemailを取得⑦AppログインAPI with email⑧Appログイン成功連携ポイント
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい44❏ 「④入力したemail 取得」の連携
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい45❏ 「④入力したemail 取得」の連携❏ JavaScript経由で取得すればよい❏ 例: WKScriptMessageHandler + WKUserScript を利用
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい46①WebView側からpostするためのmessageHandlerNameを定義
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい47②ログインフォームにEventListenerを設定するJavaScriptを記述
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい48③ログインボタン押下時、 messageHandlerNameに対してpostMessage(送信)する
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい49④JavaScriptをWebViewにInjection(挿入)する
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい50⑤WebView側からのpostを受けるためのmessageHandlerNameを登録
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい51⑥Delegateメソッド内でmessageHandlerNameを受信し、emailを取得
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい52❏ 「⑥ECログイン成功」の連携(これが問題)
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい53❏ 「⑥ECログイン成功」の連携(これが問題)❏ ログイン成功時にWKScriptMessageHandler経由での連携❏ 今回、外部Webコンテンツのため、この手法が取れない
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい54❏ 「⑥ECログイン成功」の連携(これが問題)❏ ログイン成功時にWKScriptMessageHandler経由での連携❏ 今回、外部Webコンテンツのため、この手法が取れない❏ ログインボタン押下?❏ 成功 or 失敗はわからない
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい55❏ 「⑥ECログイン成功」の連携(これが問題)❏ ログイン成功時にWKScriptMessageHandler経由での連携❏ 今回、外部Webコンテンツのため、この手法が取れない❏ ログインボタン押下?❏ 成功 or 失敗はわからない❏ URL遷移?❏ ログイン成功する場合、/login -> / のような遷移
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい56❏ 「⑥ECログイン成功」の連携(これが問題)❏ ログイン成功時にWKScriptMessageHandler経由での連携❏ 今回、外部Webコンテンツのため、この手法が取れない❏ ログインボタン押下?❏ 成功 or 失敗はわからない❏ URL遷移?❏ ログイン成功する場合、/login -> / のような遷移❏ Go Back時も/login -> / のような遷移になる?
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい57❏ 「⑥ECログイン成功」の連携(これが問題)❏ ログイン成功時にWKScriptMessageHandler経由での連携❏ 今回、外部Webコンテンツのため、この手法が取れない❏ ログインボタン押下?❏ 成功 or 失敗はわからない❏ URL遷移?❏ ログイン成功する場合、/login -> / のような遷移❏ Go Back時も/login -> / のような遷移になる?❏ ログインボタンタップ後、/ に遷移すれば良い?
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい58❏ 「⑥ECログイン成功」の連携(これが問題)❏ ログイン成功時にWKScriptMessageHandler経由での連携❏ 今回、外部Webコンテンツのため、この手法が取れない❏ ログインボタン押下?❏ 成功 or 失敗はわからない❏ URL遷移?❏ ログイン成功する場合、/login -> / のような遷移❏ Go Back時も/login -> / のような遷移になる?❏ ログインボタンタップ後、/ に遷移すれば良い?→良さそう
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい59❏ 「⑥ECログイン成功」の連携(これが問題)❏ ログイン成功時にWKScriptMessageHandler経由での連携❏ 今回、外部Webコンテンツのため、この手法が取れない❏ ログインボタン押下?❏ 成功 or 失敗はわからない❏ URL遷移?❏ ログイン成功する場合、/login -> / のような遷移❏ Go Back時も/login -> / のような遷移になる?❏ ログインボタンタップ後、/ に遷移すれば良い?→良さそう❏ ログインボタンタップ後の遷移ということはどう判断する?フラグ?
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい60❏ ログインボタン押下後の遷移(/login -> /)はハンドリングできそうだが…❏ ログイン方法が複数ある場合、それぞれに対応する必要あり❏ email/pass❏ SNSログイン❏ …
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい61❏ ログインボタン押下後の遷移(/login -> /)はハンドリングできそうだが…❏ ログイン方法が複数ある場合、それぞれに対応する必要あり❏ email/pass❏ SNSログイン❏ …❏ URL遷移なので色々な遷移の可能性がある❏ Go Backしたら?❏ バリデーションで引っ掛かったら?❏ ReCaptchaに阻まれたら?❏ …
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい62❏ 実際にはログインだけではなく、登録やその他連携も必要❏ これらを全てを素直にハンドリングするのは大変…❏ WebView関連のコードがFatになってくるのは想像に難くない…
事例:外部のECサイトをアプリに組み込んで、会員情報・認証情報をアプリと同期させたい63❏ 実際にはログインだけではなく、登録やその他連携も必要❏ これらを全てを素直にハンドリングするのは大変…❏ WebView関連のコードがFatになってくるのは想像に難くない…↓そこでステートマシンを利用しよう!
ステートマシンを活用した状態管理12.5
ステートマシンを活用した状態管理65❏ ステートマシンとは?❏ 入力条件と現在の状態によって次の状態が決まる論理回路❏ ステートマシンの構成要素❏ 状態(State)❏ イベント(Events)❏ 遷移(Transitions)❏ + 副作用(Side-Effects)
ステートマシンを活用した状態管理66❏ ログインフローにステートマシンを適応し、モデル化を行うとどうなるか?
ステートマシンを活用した状態管理67
ステートマシンを活用した状態管理68
ステートマシンを活用した状態管理69
ステートマシンを活用した状態管理70
ステートマシンを活用した状態管理71
ステートマシンを活用した状態管理72API実行もSideEffectとして定義する
ステートマシンを活用した状態管理73
ステートマシンを活用した状態管理74
ステートマシンを活用した状態管理75❏ ステート図(StateDiagram)を書くとロジックがシンプルに記述できる❏ 「State(before) -> State(afger) : Event / SideEffect 」の組み合わせ
ステートマシンを活用した状態管理76❏ ステート図(StateDiagram)を書くとロジックがシンプルに記述できる❏ 「State(before) -> State(afger) : Event / SideEffect 」の組み合わせ❏ 実はステートマシンとWebViewのステート管理は相性が良い❏ 構成要素❏ 状態(State)❏ どのURLが表示されているか❏ ログイン処理状況❏ イベント(Events)❏ 読み込み開始/終了❏ ボタンイベントリスナー❏ 副作用(Side-Effects)❏ APIリクエスト❏ エラー表示
具体的な実装方法15.5
具体的な実装方法78❏ ここではステートマシンの仕組み自体の実装方法は本題ではないのでステートマシンは以下のライブラリを利用します。❏ marcy731/StateMachine❏ https://github.com/marcy731/StateMachine❏ Point❏ TypeSafeで軽量なライブラリ❏ 必要十分な機能❏ 副作用の管理
具体的な実装方法79❏ ステートマシン の実装手順1) ステート図(State Diagram)を作成2) State / Event / SideEffect を定義3) Transitionを定義4) StateMachineインスタンス生成 / SideEffect の実装5) 適切なタイミングでEventを発火
具体的な実装方法 - ステート図(State Diagram)を作成 -80
具体的な実装方法 - State / Event / SideEffectを定義 -81
具体的な実装方法 - State / Event / SideEffectを定義 -82
具体的な実装方法 - State / Event / SideEffectを定義 -83
具体的な実装方法 - State / Event / SideEffectを定義 -84
具体的な実装方法 - State / Event / SideEffectを定義 -85
具体的な実装方法 - State / Event / SideEffectを定義 -86
具体的な実装方法 - State / Event / SideEffectを定義 -87
具体的な実装方法 - Transitionを定義 -88
(再掲)ステートマシンを活用した状態管理89
❏ StateDiagramの記述と同じように記述ができる❏ StateMachineの定義❏ StateDiagramの定義❏ StateDiagramさえできれば、実装が終わったといっても過言ではない具体的な実装方法 - Transitionを定義 -90
具体的な実装方法 - StateMachine インスタンス生成 / SideEffect の実装 -91
具体的な実装方法 - StateMachine インスタンス生成 / SideEffect の実装 -92初期Stateと定義したtransitionsを与えてStateMachineインスタンスを生成
具体的な実装方法 - StateMachine インスタンス生成 / SideEffect の実装 -93クロージャー内はTransition時に呼ばれる
具体的な実装方法 - StateMachine インスタンス生成 / SideEffect の実装 -94ClosureにはTransitionの結果がResult型で渡る
具体的な実装方法 - StateMachine インスタンス生成 / SideEffect の実装 -95エラーハンドリング:ex: 未定義のTransitionのエラー処理
具体的な実装方法 - StateMachine インスタンス生成 / SideEffect の実装 -96SideEffectのアクションの実装
具体的な実装方法 - Event の発火 -97❏ State / Event / Transition / SideEffect を定義したら、適切なタイミングでEventを発火させれば良い。❏ タイミングは「WebView-ネイティブ間連携の基礎知識」参照例❏ トップページ読み込み完了時❏ ログインページ読み込み完了時
具体的な実装方法 - Event の発火 -98❏ State / Event / Transition / SideEffect を定義したら、適切なタイミングでEventを発火させれば良い。❏ タイミングは「WebView-ネイティブ間連携の基礎知識」参照例❏ トップページ読み込み完了時❏ ログインページ読み込み完了時「トップページ読み込み完了」のEventを発火
❏ このように適切なタイミングでEventを発火さえすれば、あとはStateMachineが自動的に処理してくれる!具体的な実装方法 - Event の発火 -99
❏ このように適切なタイミングでEventを発火さえすれば、あとはStateMachineが自動的に処理してくれる!→ 複雑なロジックの記述が不要❏ ステートマシンを定義すればOK→ シンプルな記述になる❏ Event発火の実装に集中できる具体的な実装方法 - Event の発火 -100
まとめ19.5
まとめ102❏ (前提)なるべくWebViewを使うことは避けたい利用する場合は、なるべくWebViewとの連携が少なくなるようにする❏ それでも連携が複雑になる場合、ステートマシンを検討しよう❏ StateDiagramを作成し、それを元にState/Event/Transition/SideEffectを定義❏ あとはStateDiagram通りに、適切なタイミングでEventを発火させるだけ❏ 幸せになれるかも…!
STORES は モバイルエンジニアを採用しています!103iOS/Androidエンジニア絶賛採用中!採用ページはこちら!URL : https://jobs.st.inc/engineer
後夜祭 iOSDC Japan 2023 へのご参加もお待ちしています!1042023/09/26 19:00-20:30https://hey.connpass.com/event/290854/
20.5
Reference106❏ WKWebView❏ https://developer.apple.com/documentation/webkit/wkwebview❏ marcy731/StateMachine❏ https://github.com/marcy731/StateMachine
Appendix107❏ WebViewをNativeのように使いたい @2023/03/22 Ebisu.mobile #1❏ https://speakerdeck.com/marcy731/using-webview-like-native-app