SyncTimer に Google Analytics を導入した

たぶん使ってる人はいるんだろうなー、と思いつつも利用報告をしてくれるユーザーはほぼいない。 それは当然で、報告するメリットもないし、不具合があったり使い勝手が悪いなーと思っても似たような手段はいくらでもあるので別のツールに切り替えるだけになる。 だったら自分で調べるしか無いよね、ということで Google Analytics を導入して実際のアクセスを調査するしか無いのである。

今回は Elm で書いたアプリケーション内のイベントをGA4に送信する実装のお話。

イベントを送信する

素直にタグを記述するだけでページへのアクセスなどは取得できるのだが、そもそもSyncTimerは1ページしか無く、 「アクセスがあった」「スクロールした」くらいの情報やOS,ブラウザの種類などが取れる程度なので、 実際にタイマーを動かしたかを知りたい場合はイベントを送信する必要がある。

GA4 - イベントを設定する

Elm側で実行されたイベントを送信するには、Elmコードの外にある gtag() 関数を呼び出す必要があるので、ここで Ports という機能を使うことになる。

ポート(Ports) - Elm

簡単に言ってしまえば外部とやり取りする方法で、「外部に文字列(String)を送る」「外部からStringを受け取る」しかない。 今回の場合はイベントを文字列で送るのだが、イベントの種類などの構造を持ったデータを送信したいので JSON 形式に変換する関数を作る。

import Json.Encode as E

encodeAnalyticsEvent : String -> String -> String -> Maybe Int -> String
encodeAnalyticsEvent category action label value =
    E.encode 0 <|
        E.object <|
            [ ( "category", E.string category )
            , ( "action", E.string action )
            , ( "label", E.string label )
            ]
                ++ (Maybe.map (\v -> [ ( "value", E.int v ) ]) value |> Maybe.withDefault [])

で、この関数で生成される文字列を port で外部に送信する

port sendAnalyticsEvent : String -> Cmd msg

timerStartEvent : Int -> Cmd msg
timerStartEvent currentTime =
    sendAnalyticsEvent <| encodeAnalyticsEvent "sync_timer" "sync_timer_start" (formatTimeForAnalytics currentTime) Nothing

外部のJSで受け取る方は JSON 文字列をパースして gtag() 関数でイベントを送信する。

app.ports.sendAnalyticsEvent.subscribe((event) => {
  const { category, action, label, value } = JSON.parse(event);
  console.debug({ category, action, label, value });
  if (gtag) {
    const data = Object.assign({
      "event_category": category,
      "event_label": label
    }, value ? { value } : {});
    gtag("event", action, data);
  }
});

という仕組みで送信されている。

おかげで利用状況がある程度わかるようになってきた。