WKScriptMessageHandler解析: WebからiOSへのメッセージ伝達の舞台裏について理解する
はじめに
本記事は、Lancers(ランサーズ) Advent Calendar 2024 の23日目の記事です。
ランサーズのiOSアプリで、WebView上のイベント(メッセージスクリプト)をトリガーにiOSアプリ側でIn-App-Reviewモーダルを表示する実装があります。
今回、ソースリーディングしたことをきっかけに、
Webから、iOS端末にJavaScriptのスクリプトを実行しメッセージを送る仕組み
を紐解いていきたいと思います。
各工程の全体像
本記事では、以下の4工程に分けて解説をしていきます。
- [iOS側]WKWebView上で表示されているWeb画面がアプリ内のイベントを処理
- [Web側]1のイベントをトリガーにJavaScriptのスクリプトが実行される
- [iOS側]2で取得したデータを、
WKUserContentController
に転送 - [iOS側]3で取得したデータを、
WKScriptMessageHandler
に処理を委譲
実装編
以下の今回の記事用に作成したデモアプリのソースコードを用いて、各工程の関連箇所について、解説していきます。
1. [iOS]WKWebView上で表示されているWeb画面からイベントを送信
WKWebViewを使用した実装は、UIKitのViewであるため、UIViewRepresentableクラスを用います。また、ViewObjectを生成したいため、makeUIView内で定義しました。
尚、ソース上にも記載の通り、今回はプロジェクトローカル上のindex.html
データを使用しております。
2. [Web]1のイベントをトリガーにJavaScriptのスクリプトが実行される
Web側の実装は以下のようにしてみました。Web画面のボタンをタップしたら、iOS側でログが出力されるような仕様になります。
htmlを表示すると、以下のようになります。
3. [iOS]2で取得したデータを、WKUserContentControllerに転送
WebViewの初期化時に必要なため、makeUIView内で定義しました。
※注意点: add(_:name:)
メソッドの引数であるname
で定義するString
型のスクリプトのネーミングは空文字以外である必要があります。
The name argument must be a non-empty string.
4. [iOS]3で取得したデータを、WKScriptMessageHandlerに処理を委譲
今回は、WebViewからスクリプトが実行された時、iOS側ではログ出力するように設定する箇所になります。
ビルドした結果を確認 💻
WebView上のボタンタップ時して、iOS側でログ出力ができていることが確認できました🎊
テスト編
Safariを使用してスクリプト実行をテストする
これまでのサンプルコードでは、かなりシンプルなスクリプトの発火条件(ボタンのタップ)でしたが、実際のプロダクトでは、「ユーザーの特定の行動に基づいて、スクリプトを実行する場合」などがあり、それを実行するために、Web側のソースコードを修正するのは、かなり非効率です。
そのような場合にSafariのDeveloper Menu表示を有効にし、以降の設定を行うと、特定のスクリプトを指定のタイミングで実行することができるため、共有させていただきます。
1. SafariのDeveloper Menu表示を有効にする
Safari > Preferences > Advancedの以下の箇所にチェック
参考:
2. SafariのWeb Inspectorを表示する
Safari > Developから、表示中のiOS SimulatorのWebViewを選択し、Web Inspectorを表示
※iOS16.4以上のSimulatorをお使いの場合、以下に記載の実装をソースコード上で行う必要があります。
参考:
3. Web InspectorのSourcesタブを選択して、スクリプトを入力&実行
// 1. メソッドを作成
// postMessage()内に、送信するメッセージを入力
function sampleFunction() {
window.webkit.messageHandlers.requestReview.postMessage('')
}
// 2. 即時実行されるように呼び出し
sampleFunction();
4. Safariを使ってスクリプトを実行できるようになりました 🎊
参考: 今回使用した主要なAPI一覧
おわりに
Lancers では、一緒に働けるメンバーを募集しています!
Discussion