Using Google Analytics in Electron Desktop Apps

I recently had to implement a comprehensive Analytics solution into one of our Desktop apps, which is built using Electron and VueJS. It was decided early on to use Google Analytics for this, due to the existing infrastructure and familiarity with the web interface.

Implementing Google Analytics on a website#

Implementing Google Analytics on any website (like this blog for example) is trivial. Simply sign up for Analytics (Google just released their new version of Analytics!), grab your measurement or Universal Analytics ID, and implement the Global site tag, or gtag.js for short, on your website:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=<YOUR_ID>"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', '<YOUR_ID>');
</script>

That’s it! Any visitor will now be tracked on your website (unless they use an ad blocker, such as uBlock Origin ) and you can view stastics on your Google Analytics dashboard.

A word about how Google Analytics works#

If you don’t know how Google Analytics works, it’s pretty simple: When someone visits your page, the Tag Manager snippet automatically sets two persistent cookies (which expire after two years, or until manually cleared by the user). With these cookies, Google is able to track current and returning visitors and give you, the site owner, very detailed information about each user (location, behavior on site, …). The persistent cookies are used to assign each visitor a unique Client ID in Google Analytics, where each Client ID equals one currently active user.

This is also why GDPR regulation requires anyone implementing Google Analytics anywhere to notify and explicitly ask for users’ consent to enable the tracking of their behavior, since the aforementioned Client ID is personally identifiable information and as such falls under GDPR regulation (setting cookies does not automatically fall under GDPR regulation).

Google Analytics and Desktop Apps#

If you’re hoping that using Google Analytics in Desktop Apps is as easy as with a website, you are in for a disappointment. Google Tag Manager works exclusively with websites, which are using either http:// or https:// protocol; Frameworks such as Electron or React instead use the file:// protocol, but also allow implementing a custom protocol (such as app://) with ease.

Using the local file:// protocol causes a few problems:

  1. Google Analytics requires requests to use http/s protocol.
  2. Electron doesn’t support storing browser cookies the same way a regular browser does.
  3. Electron has to send a proper URL to the Google Analytics server (including protocol, which goes back to point 1).

You are able to tell Google Analytics to ignore the file:// protocol, but this workaround requires setting up a new tag in Google Tag Manager and linking said tag with your Universal Analytics / Google Analytics 4 Property. Afterwards, you have to create an empty JavaScript function and assign that to a custom variable inside Google Tag Manager. Now, you can use Fields To Set in your tag and define the Field Name checkProtocolTask, with the Field Value being the newly created empty JavaScript function.

With this workaround, however, the other two problems still remain. Instead of circumventing them via custom variables inside Google Tag Manager, there is a different solution entirely.

Enter the Measurement Protocol#

The Measurement Protocol allows you to send raw data directly to Google Analytics via POST Requests, working around all three requirements listed above. Each data point is called a hit, so a page view would actually be called a page hit. Usually, you will only be sending page and event hits, but you could also send e-commerce hits to analyze product impressions or similar data. In this article, we will focus exclusively on page and event hits.

The Measurement Protocol Endpoint#

The full URL for the POST Request is https://google-analytics.com/collect. This endpoint does not accept regular JSON data; instead, we will have to UTF8 and URL Encode data we send and make sure one hit is sent on one line. As you’d expect, there is a minimum amount of data you have to send with every request:

v=1              // Version.
&tid=UA-XXXXX-Y  // Tracking ID / Property ID.
&cid=555         // Anonymous Client ID.
&t=              // Hit Type.

Where t= is either pageview or event for page hits and event hits respectively. Version always equals 1, since it is the Measurement Protocol version and not a Google Tag version or similar.

You will notice that tid= starts with UA-, which denotes a Universal Analytics Property. Unfortunately, Measurement Protocol v2 is still somewhat far out, since Google Analytics 4 Properties were just released. In the meantime, you will have to stick to the “old” Google Analytics in your Desktop Apps. This is one of the major drawbacks of using the Measurement Protocol.

Setting the proper Client ID#

As I mentioned previously, Google Analytics sets two persistent cookies per default, which include the personalised Client ID. When using Measurement Protocol, the responsibility for generating and maintaining a unique, persistent and personalised Client ID is instead shifted over to you. Google specifies that the Client ID should be a UUID version 4 as per RFC 4122 . In practice, if you are using npm or yarn, I recommend using the uuid package to do the heavy lifting here.

Sending a basic request to Measurement Protocol Endpoint#

For this example, we’ll be using TypeScript. To send a basic pageview hit via Measurement Protocol, we can use URLSearchParams() and axios :

import { v4 as uuidv4 } from 'uuid';

const payload = new URLSearchParams({
    v: 1,
    cid: uuidv4(),
    tid: 'UA-12345-1',
    t: 'pageview',
    dp: '/blog',
    dt: 'Blog'
}).toString();
axios.post('https://google-analytics.com/collect', payload);

(Note: In older versions of TypeScript (below v4), you may have to cast the object inside URLSearchParams() as any)

After sending the above request (which you could also implement using fetch ), your Google Analytics Dashboard should now show one active user on the path given by the dp parameter.

Note that this user does not actually have to exist; you could simply send a bunch of fake data to your own Analytics dashboard.

Track User Pageviews#

So how do we track our actual users inside the application? If you are using VueJS, you can use vue-router to intercept the request after it is done and automatically send a pageview hit to Google Analytics.

Before we can set up the navigation guard, we will have to configure a name for our route, like so:

{
    path: '/blog',
    name: 'Blog',
    component: () => import(/* component ... */)
}

With the name for the route configured, we can use vue-router’s afterEach navigation guard to send the pageview hit:

import { v4 as uuidv4 } from 'uuid';

const cid = uuidv4();
const tid = 'UA-12345-1';

router.afterEach(to => {
    if (!to.name) return;

    const baseUrl: string = vueRouter.options.base || "";
    let fullUrl: string = baseUrl;
    if (!fullUrl.endsWith("/")) {
        fullUrl += "/";
    }
    fullUrl += to.fullPath.startsWith("/") ? to.fullPath.substr(1) : to.fullPath;

    const payload = new URLSearchParams({
        v: 1,
        cid,
        tid,
        t: "pageview",
        dp: fullUrl,
        dt: to.name
    }).toString();
    axios.post('https://google-analytics.com/collect', payload);
}

You will now be able to track which pages your users visit inside your Electron application.

Tracking individual events#

Let’s say you want to track user behavior during registration inside your app, possibly in combination with A/B testing. You’ve successfully tracked the user entering the page, but you probably also want to track when/if a user starts entering their e-mail address and finally clicks that “Register” button. For that, you will need event hits.

For illustration purposes, let’s build a button that fires off a click event inside a Vue component we’ll call register.vue:

<template>
    <button type="submit" @click="trackEvent()">Register</button>
</template>

<script lang="ts">
export default {
    name: 'RegisterButton',
}
</script>

<script setup>
    import { v4 as uuidv4 } from 'uuid';

    const cid = uuidv4();
    const tid = 'UA-12345-1';
    const trackEvent = () => {
        const payload = new URLSearchParams({
            v: 1,
            cid,
            tid,
            t: 'event',
            ec: 'button_press',
            ea: 'press'
        }).toString();
        axios.post('https://google-analytics.com/collect', payload);
    }
</script>

Using Vue 3 syntax, we are able to move the Setup function, available thanks to the new Vue Composition API , to its own script tag. You can also define the trackEvent function as its own method using the old Vue 2 syntax.

Let’s unpack the relevant bits. When the user presses the button, a click event it sent to our trackEvent function. Here, we set the Event hit type to event. We also define two new parameters: ec, which is the Event Category, and ea, which is the Event Action. There’s also two optional parameters, which I did not send: el (Event Label) allows additional filtering with labels on your Analytics dashboard, and using ev (Event Value), you can send values such as user IDs or timestamps over to your Analytics Dashboard, if needed.

The Event Category and Event Action are the first two values you will see in your Analytics Dashboard (for example, in the new dashboard, user_engagement is a regular Event Category you will immediately see when someone visits your page). The optional values will only show up when expanding into the “main” Events view.

Conclusion#

Using the Measurement Protocol, it is fairly simple to send custom page view and event hits to your Google Analytics Dashboard. Since you are able to customise certain values, like the name of the Event Category, you gain a lot of flexibility when deciding which events show up on your dashboard.

You can, of course, expand on the above examples. In my case, I created two TypeScript interfaces for both Page and Event Hits and streamlined other bits, like the POST request and handling of the unique Client IDs, so I only have a single instance of an Analytics class that I use to send both Page and Event hits to my dashboard.

There are of course downsides to using the Measurement Protocol, with the most notable being that you have to track everything individually, from button presses to page views. In addition, you will have to create and maintain your own unique Client ID for every user, although there are libraries to help with that.

Overall, I’m really glad I found out about the Measurement Protocol, since I have not found any other solution to implementing Google Analytics inside Electron Apps so far.

Further reading#

Working with the Measurement Protocol (Google)

© 2020 Søren Johanson