【GA4】GTMを使ってアプリストリームからWebView時のページビューを計測する

# 本記事でできるようになること
- WebViewで開かれたページビュー情報をGA4アプリストリームで計測する
- WebViewで開かれたページビュー情報をGA4ウェブストリームで計測しないようにする
- WebViewでページが開かれたことを判別できるGTMトリガー設定を作成する

# 背景
GA4の計測設定を自社サイトと自社アプリの両方で行った場合、WebView（アプリ内ブラウザ）でGA4タグを設置しているページを開くと以下のような計測となってしまいます。

- アプリストリームによる計測 : WebViewで開いたページ情報は追うことができない
- ウェブストリームによる計測 : WebViewからページが開かれても判別できずウェブストリームからデータを送信してしまう

→ 同一ユーザーによる一連の行動が「異なるユーザーによる行動」としてバラバラに計測されてしまう

![WebViewイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/76651221-4292-bc82-d8a1-f2a45beed60e.png)

<!--
（アプリとWebの両方で共通のユーザーIDにより[userId計測設定を行う](https://support.google.com/analytics/answer/9213390)ことで同一ユーザーの行動として計測することは可能ですが、アプリとウェブでそれぞれ別軸の行動として計測されてしまいます）
-->

また、単純にWebView時のページビューなどのイベントをアプリストリームで計測できるようにするだけではアプリストリームとウェブストリームでの二重計測を起こしてしまう場合があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/3501d282-a685-b530-ce9a-23a3a855495d.png)

本記事ではこれらの問題の発生を防ぎつつ、WebViewでの行動をアプリストリームで計測するための方法をご紹介します。

# 手順
## 1. アプリストリームの計測設定を用意する・WebView計測の準備を行う
記事の手順を参考に以下の対応を行います。

- Firebase SDKをアプリに導入
- FirebaseプロジェクトをGoogleアナリティクス（GA4プロパティ）に接続
- Webサイト側にJavaScript ハンドラを実装（WebViewで開かれる可能性があるページすべて）
- アプリ側にネイティブ インターフェースを実装

これらの準備によりWebView以外のGA4アプリ計測環境が整います。この手順は以下の記事を参考に進めてください。

https://qiita.com/aqril_1132/items/5997fc611ee227e7a567

JavaScriptハンドラはウェブページに直接埋め込むこともできますが、GTM内を介して設定設定できます。
ページに直接埋め込む場合は必ずGTMより先に読み込まれるようにしてください。

### GTMにJavaScriptハンドラを実装する場合
Google公式ヘルプにあるJavaScriptハンドラコードをカスタムHTMLタグとしてGTMに実装して後述の「WebView時にGA4アプリストリームに情報を送信するタグ」の「[設定タグ](https://support.google.com/tagmanager/answer/6238868)」として「[1ページにつき1回](https://support.google.com/tagmanager/answer/6279951)」起動するように設定。
https://firebase.google.com/docs/analytics/webview?platform=android#implement-javascript-handler
```html:
<script>
if(typeof logEvent === 'undefined'){
  function logEvent(name, params) {
    if (!name) {
      return;
    }
  
    if (window.AnalyticsWebInterface) {
      // Call Android interface
      window.AnalyticsWebInterface.logEvent(name, JSON.stringify(params));
    } else if (window.webkit
        && window.webkit.messageHandlers
        && window.webkit.messageHandlers.firebase) {
      // Call iOS interface
      var message = {
        command: 'logEvent',
        name: name,
        parameters: params
      };
      window.webkit.messageHandlers.firebase.postMessage(message);
    } else {
      // No Android or iOS interface found
      console.log("No native APIs found.");
    }
  }
}
if(typeof setUserProperty === 'undefined'){
  function setUserProperty(name, value) {
    if (!name || !value) {
      return;
    }

    if (window.AnalyticsWebInterface) {
      // Call Android interface
      window.AnalyticsWebInterface.setUserProperty(name, value);
    } else if (window.webkit
        && window.webkit.messageHandlers
        && window.webkit.messageHandlers.firebase) {
      // Call iOS interface
      var message = {
        command: 'setUserProperty',
        name: name,
        value: value
     };
      window.webkit.messageHandlers.firebase.postMessage(message);
    } else {
      // No Android or iOS interface found
      console.log("No native APIs found.");
    }
  }
}
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/36f9b4d2-2b0e-a7b7-8d2e-3d83cd8e0c5f.png)


## 2. GA4ウェブストリーム向け計測タグの設定を行う
アプリの計測データが記録される状態になったGA4プロパティにウェブの計測データも記録されるように設定します。

### Firebase Console画面での作業
[Firebase Console](https://console.firebase.google.com/)からGA4の管理画面を開きます。

目的のFirebaseプロジェクト画面で「左側の歯車アイコン」→「プロジェクトを設定」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/5c96e1f4-f32c-043b-0ade-fe584980a583.png)
タブ「統合」をクリックしてGoogle Analyticsの「管理」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/7ae256f6-f30d-82ad-06a9-2c2c9f68410b.png)
「リンクされたGoogleアナリティクスアカウント」からアプリが紐付いているGA4プロパティの管理画面を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/8e2208da-ad00-4919-2be9-2f3973bbb461.png)
### GA4管理画面内での作業
表示されたGA4のプロパティ設定画面の「データストリーム」メニューを開き、「ストリームを追加」プルダウンメニューの「ウェブ」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/5c70c116-e6a8-455f-bf17-6a35804bf3a5.png)
測定対象サイトのURLとわかりやすいストリーム名を入力して「ストリームを作成」ボタンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/aef1e228-a631-6afe-9218-4ae152b39b1d.png)

次の手順で利用するため、表示された**測定ID**の情報をどこかに記録しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/53de5c8f-b8c3-4e3f-7a58-03f09424b6c5.png)
### GTM内での作業
GA4ウェブストリーム向け計測タグは後々の設定調整を考えるとGTM経由で設定する方が便利なので、GTMを利用した設定方法で進めます。
#### GA4のタグでページビュー計測するための基本設定
タグタイプ「Google アナリティクス: GA4 設定」でタグ設定を新規作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/433e1b80-b952-024b-677a-a05f2e9e64e7.png)

先ほどGA管理画面から入手した測定IDを入力して適切なタグ名とトリガー設定を追加して一旦設定を保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/f2ad3958-3eb5-a305-22fe-0d34d9a5929d.png)


#### GA4アプリストリームにデータを送ることができることをチェックする変数とトリガー設定を作成
ここまでの手順でアプリ側にGA4計測用の[ネイティブインターフェイス](https://firebase.google.com/docs/analytics/webview?platform=android#implement_native_interface)が実装されているので、AndroidアプリのWebView経由でサイトを閲覧した場合のみページ内にグローバルJavaScript変数`window.AnalyticsWebInterface`が存在。iOSアプリのWebView経由でサイトを閲覧した場合のみ`window.webkit.messageHandlers.firebase`が存在するようになっています。
この有無をGTMから確認してGA4アプリストリームにデータを送ることができる状況かの判定に使用することが可能です。
他社アプリのWebViewからページが開かれる可能性を考慮し、UserAgent文字列などで自社アプリ判定も加えます。

**例: 自社アプリのUserAgent文字列に `my-app` という文字列が入る場合**

```js:Is_browsing_through_MyApp_with_Firebase_Analytics
function(){
  var isNativeInterfaceAvailable = !!(window.AnalyticsWebInterface || window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.firebase);
  var isAccessFromOfficialApp = navigator.userAgent.indexOf('my-app')>-1;
  return isNativeInterfaceAvailable && isAccessFromOfficialApp;
}
```

**変数設定の作成例**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/c1dce28a-5a6d-93aa-d713-7313ba5cceb8.png)


作成した変数設定を利用してWebView経由でのページ閲覧時に発生したすべてのイベントに反応するトリガー設定を作成します。

- イベント名 : `.*`
- 「正規表現一致を使用」にチェック
- このトリガーの発生場所 : 一部のカスタムイベント
- 条件 : 「先ほど作成したWebViewを判定する変数設定」が「等しい」`true`

**トリガー設定の作成例**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/f524ea6d-713a-e418-c114-d0bf4995804a.png)


作成したトリガーをウェブストリーム向けGA4タグ設定の例外トリガー設定に追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/c0afafcc-9ec6-68da-d2a5-6c4746bdbfa6.png)


以上でGA4ウェブストリームにpage_viewイベントを送信するための設定が完了しました。


## 3. WebView時にGA4アプリストリームに情報を送信するタグ設定を行う
GA4のアプリストリームにWebView時のページビュー情報を送信するイベントについて予め使用するイベント名とパラメータ構成を決めておきます。



本記事ではカスタムイベント`wv_page_view`と以下のパラメータを使う場合の例をご紹介いたします。
パラメータは必要に応じて予め[GA4管理画面側でカスタムディメンションに登録](https://support.google.com/analytics/answer/10075209?hl=ja)しておきます。

イベント : wv_page_view
パラメータ

| パラメータ名 | 送信する値 | 送信する値の例 |
|:--|:--|:--|
| wv_page_location | 閲覧中のページURL | https://www.example.com/path/page.html?a=b#hoge |
| wv_page_hostname | 閲覧中のページURL | www.example.com |
| wv_page_path | 閲覧中のページURL | /path/page.html |
| wv_referrer | 参照元ページURL | https://www.example.com/path/prevpage.html?zz=nn |
| wv_page_title | 閲覧中のページタイトル | ○○○についてのページ |


以下のようなコードをカスタムHTMLタグで使用することにより、アプリストリームにwv_page_viewイベントを送信するタグを作成できます。必要なパラメータが他にある場合はコードを編集してご利用下さい。

```html:GA4_for_app_-_wv_page_view
<script>
(function(){
  if(typeof logEvent !== 'undefined'){
    var EVENT_NAME = 'wv_page_view';
    var params = {
      'wv_page_location': location.href,
      'wv_page_hostname': location.hostname,
      'wv_page_path': location.pathname,
      'wv_referrer': document.referrer,
      'wv_page_title': document.title,
    };
    for(key in params){
      if(typeof params[key] === 'string'){
        // パラメータの値が100文字を超えるとエラーになるので超過分の文字をカット
        params[key] = params[key].substr(0,100);
      }
    }
    logEvent(EVENT_NAME, params);
  }
})();
</script>
```


JavaScriptハンドラをGTM経由で実装する場合は「[設定タグ](https://support.google.com/tagmanager/answer/6238868)」としてJavaScriptハンドラを実装したカスタムHTMLタグを読み込ませます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/6ba0f139-fc46-34aa-cc3c-ca3bc5433428.png)

必要なトリガー設定を紐づければ完成です。今回の用途であれば初期化トリガーを使用します。
こちらについては例外トリガー条件を設定しなくても問題ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/675fde97-c9e5-12ef-b68f-5522c56a4137.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/56bd503b-e350-b308-89b7-ffbe7c337666.png)

<!--
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/eee3abf6-493c-7158-cf2c-d7031a31a291.png)
-->

以上でGA4アプリストリームにwv_page_viewイベントを送信するための設定が完了しました。
GTMの変更内容の公開を忘れずに行いましょう。
<!--
![スライド3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/21112c79-afc4-83b6-b91a-20e88f54d2e5.png)

**GA4デバッグビューによるアプリストリームからのpage_viewイベント送信確認**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/2424ee8f-ac4e-ce28-98b1-bb4fd8e6d823.png)

**BigQueryにエクスポートされたデータ**
1行目がウェブストリームからのpage_viewイベント。2行目がアプリストリームからのpage_viewイベント。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103957/077d75e5-b5d9-0aa2-f3ab-f29dcb829874.png)

-->

<!--
また、同様の方法でview_item(商品詳細情報の閲覧)などのイベントをアプリストリームで計測するタグ設定を作成することも可能ですが、ネイティブインターフェイスのコードを書き換える必要があります。

**view_itemイベントをアプリストリームに送信するカスタムHTMLタグのコードサンプル**

```html:GA4_for_app_-_view_item
<script>
  if(typeof logEvent !== 'undefined'){
    logEvent('view_item',{
      currency: 'JPY',
      items: [{
        item_id: '{{商品ID}}',
        item_name: '{{商品名}}',
        item_brand: '{{商品ブランド}}',
        item_category: '{{商品カテゴリ}}',
        item_variant: '{{商品バリエーション}}',
        price: {{価格}},
        currency: 'JPY',
        quantity: {{数量}}
      }],
      value: {{価格}}
    });
  }
</script>
```
Google Analytics 4 Events  |  Global site tag (gtag.js)
https://developers.google.com/gtagjs/reference/ga4-events#view_item
※ 日本語版の情報は古い場合があるので英語版での閲覧をオススメします
-->

# その他小ネタ
## パラメータ
- WebViewでのページ閲覧を`screen_view`イベントとして送信する場合、以下のパラメータでスクリーン名、スクリーンクラスを上書き可能
    - パラメータ`screen_name` (debugView上では`firebase_screen`と表示)
    - パラメータ`screen_class` (debugView上では`firebase_screen_class`と表示)
- 公式ヘルプに記載されているネイティブインターフェースは`items`パラメータに対応していないためeCommerceイベントを計測したい場合はカスタマイズが必要。Kotlinのカスタム例は[コチラ](https://qiita.com/aqril_1132/items/5997fc611ee227e7a567#ecommerce%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%92%E8%A8%88%E6%B8%AC%E3%81%97%E3%81%9F%E3%81%84%E3%81%A8%E3%81%8D)
- WebView経由で `page_view` イベントを送信することも可能だがレポートを壊す恐れがあるため非推奨
- `screen_view` イベントのパラメータとして、 `page_location` , `page_referrer`, `page_title`を使用することができる。（現在のところ問題は確認できていませんが使用は自己責任でお願いします。）この時、レポート上で各ディメンションの値は以下のようになる
    - ページパスとスクリーンクラス: スクリーンクラスの情報が優先して使われる。取得できない場合 `page_location` 情報から抽出したページパスの文字列
    - ページタイトルとスクリーンクラス: `page_title`の情報が優先して使われる。取得できない場合、スクリーンクラス（`screen_class`）の情報が使われる
    - ページタイトルとスクリーン名: スクリーン名（`screen_name`）情報が優先して使われる。取得できない場合、`page_title`
## 検証時
- WebView計測のためのGTM設定はGTMプレビューモードによる公開前確認ができない。検証が必要な場合は例外トリガー設定で開発環境のみでタグが動作するよう設定の上公開する
- アプリ内ブラウザを実現する技術にはWebViewの他にiOSの[SafariViewController](https://developer.apple.com/jp/news/?id=trjs0tcd)、Androidの[Chrome CustomTabs](https://developers-jp.googleblog.com/2021/02/blog-post_8.html)が存在するが、いつものSafari, Chromeブラウザの別タブのような存在なのでネイティブインターフェースが効かず、これらはWebストリーム側で計測する他ない
- Android端末とWindows PCでチェックする方法 -> https://qiita.com/aqril_1132/items/f4a5077998168e50350b
- WebView計測がうまくいかないときのトラブルシューティングはやや難易度高め。エンジニアと連携して以下の内容を1つ1つチェックする
    - WebView時にGTMは正常に動いているか（アプリから開いたときにGTMが動かない実装になっていないか）
    - WebView時にタグのトリガー条件やパラメータの値に使う変数が取得できるか
    - WebView時にタグが動くより先にJavaScript HandlerによりlogEvent関数が定義されているか
    - WebView時にタグが呼び出されるためのトリガー条件が満たされているか(User-Agent文字列の変更が一部のWebViewで機能していなかったなど)
    - WebView時にアプリのネイティブインターフェースにより以下の関数が定義されているか
        - Androidの場合 : window.AnalyticsWebInterface
        - iOSの場合 : window.webkit.messageHandlers.firebase
    - アプリが紐づいているGA4プロパティとウェブストリーム計測を行うGA4プロパティは同一か