🤖

WKScriptMessageHandler解析: WebからiOSへのメッセージ伝達の舞台裏について理解する

2024/12/23に公開

はじめに

本記事は、Lancers(ランサーズ) Advent Calendar 2024 の23日目の記事です。

ランサーズのiOSアプリで、WebView上のイベント(メッセージスクリプト)をトリガーにiOSアプリ側でIn-App-Reviewモーダルを表示する実装があります。

https://apps.apple.com/jp/app/ランサーズ-副業-在宅ワークでスキルを活かしてお金を稼ぐなら/id1331949158

今回、ソースリーディングしたことをきっかけに、

Webから、iOS端末にJavaScriptのスクリプトを実行しメッセージを送る仕組み

を紐解いていきたいと思います。

各工程の全体像

本記事では、以下の4工程に分けて解説をしていきます。

  1. [iOS側]WKWebView上で表示されているWeb画面がアプリ内のイベントを処理
  2. [Web側]1のイベントをトリガーにJavaScriptのスクリプトが実行される
  3. [iOS側]2で取得したデータを、WKUserContentControllerに転送
  4. [iOS側]3で取得したデータを、WKScriptMessageHandlerに処理を委譲

実装編

以下の今回の記事用に作成したデモアプリのソースコードを用いて、各工程の関連箇所について、解説していきます。

https://github.com/ShingHikaruLancers/iOSWebMessageBridge/blob/332f54bbcc5c2c1c674e457792f81a9ed65ad4a7/WebViewMessageApp/ContentView.swift#L10-L19

1. [iOS]WKWebView上で表示されているWeb画面からイベントを送信

WKWebViewを使用した実装は、UIKitのViewであるため、UIViewRepresentableクラスを用います。また、ViewObjectを生成したいため、makeUIView内で定義しました。

尚、ソース上にも記載の通り、今回はプロジェクトローカル上のindex.htmlデータを使用しております。

https://github.com/ShingHikaruLancers/iOSWebMessageBridge/blob/5755911d8b42ab8a51eb5da696b7c5139d27b453/WebViewMessageApp/WebView.swift#L20-L38

2. [Web]1のイベントをトリガーにJavaScriptのスクリプトが実行される

Web側の実装は以下のようにしてみました。Web画面のボタンをタップしたら、iOS側でログが出力されるような仕様になります。

https://github.com/ShingHikaruLancers/iOSWebMessageBridge/blob/5755911d8b42ab8a51eb5da696b7c5139d27b453/WebViewMessageApp/WebAssets/index.html#L1-L23

htmlを表示すると、以下のようになります。

3. [iOS]2で取得したデータを、WKUserContentControllerに転送

WebViewの初期化時に必要なため、makeUIView内で定義しました。
※注意点: add(_:name:)メソッドの引数であるnameで定義するString型のスクリプトのネーミングは空文字以外である必要があります。

The name argument must be a non-empty string.

https://developer.apple.com/documentation/webkit/wkusercontentcontroller/add(_:name:)

https://github.com/ShingHikaruLancers/iOSWebMessageBridge/blob/5755911d8b42ab8a51eb5da696b7c5139d27b453/WebViewMessageApp/WebView.swift#L12-L18

4. [iOS]3で取得したデータを、WKScriptMessageHandlerに処理を委譲

今回は、WebViewからスクリプトが実行された時、iOS側ではログ出力するように設定する箇所になります。

https://github.com/ShingHikaruLancers/iOSWebMessageBridge/blob/5755911d8b42ab8a51eb5da696b7c5139d27b453/WebViewMessageApp/WebView.swift#L47-L54

ビルドした結果を確認 💻

WebView上のボタンタップ時して、iOS側でログ出力ができていることが確認できました🎊

テスト編

Safariを使用してスクリプト実行をテストする

これまでのサンプルコードでは、かなりシンプルなスクリプトの発火条件(ボタンのタップ)でしたが、実際のプロダクトでは、「ユーザーの特定の行動に基づいて、スクリプトを実行する場合」などがあり、それを実行するために、Web側のソースコードを修正するのは、かなり非効率です。

そのような場合にSafariのDeveloper Menu表示を有効にし、以降の設定を行うと、特定のスクリプトを指定のタイミングで実行することができるため、共有させていただきます。

1. SafariのDeveloper Menu表示を有効にする

Safari > Preferences > Advancedの以下の箇所にチェック

参考:
https://developer.apple.com/documentation/safari-developer-tools/enabling-developer-features

2. SafariのWeb Inspectorを表示する

Safari > Developから、表示中のiOS SimulatorのWebViewを選択し、Web Inspectorを表示

iOS16.4以上のSimulatorをお使いの場合、以下に記載の実装をソースコード上で行う必要があります。

https://iridge-tech.hatenablog.com/entry/2024/03/21/120000

参考:
https://developer.apple.com/documentation/safari-developer-tools/web-inspector

3. Web InspectorのSourcesタブを選択して、スクリプトを入力&実行


// 1. メソッドを作成
// postMessage()内に、送信するメッセージを入力
function sampleFunction() {
  window.webkit.messageHandlers.requestReview.postMessage('')
}

// 2. 即時実行されるように呼び出し
sampleFunction();

4. Safariを使ってスクリプトを実行できるようになりました 🎊

参考: 今回使用した主要なAPI一覧

https://developer.apple.com/documentation/webkit/wkwebview

https://developer.apple.com/documentation/webkit/wkusercontentcontroller

https://developer.apple.com/documentation/webkit/wkscriptmessagehandler

おわりに

Lancers では、一緒に働けるメンバーを募集しています!
https://recruit.jobcan.jp/lancers01/list/all/all

ランサーズ株式会社

Discussion