push-notificationservice-workerweb-pushprogressive-web-appspush-api

How to add analytics for Push notifications


I'm working on progressive web app, and I want to implement analytics for Push notifications.

How can I add analytics for push notifications so that I'll be able to track and record how many people clicked on notification and how many people close that notification without clicking on it.


Solution

  • I've written a small chunk of code to use Google analytics for analytics and it works fairly well.

    Dumped notes here: https://gauntface.com/blog/2016/05/01/push-debugging-analytics


    The way I've done this is the post mentioned above:

    In the service worker I import a javascript file that does the tracking for me, set the analytics ID and then in the appropriate events call the tracking. Look for self.analytics.trackEvent:

    importScripts('./scripts/analytics.js');
    
    self.analytics.trackingId = 'UA-77119321-2';
    
    self.addEventListener('push', function(event) {
      let notificationTitle = 'Hello';
      const notificationOptions = {
        body: 'Thanks for sending this push msg.',
        icon: './images/icon-192x192.png',
        tag: 'simple-push-demo-notification'
      };
    
      // Important to trigger analytics asynchronously with logic
      // to show notification
      event.waitUntil(
        Promise.all([
          self.analytics.trackEvent('push-received'),
          self.registration.showNotification('Hello', notificationOptions)
        ])
      );
    });
    
    self.addEventListener('notificationclick', function(event) {
      event.notification.close();
    
      // Important to trigger analytics asynchronously with logic
      // to do other work (i.e. open window)
      event.waitUntil(
        Promise.all([
          self.analytics.trackEvent('notification-click'),
          clients.openWindow('https://gauntface.github.io/simple-push-demo/')
        ])
      );
    });
    

    The code to do the actual tracking calls to Google Analytics Measurements Protocol is shown below. The API is painfully simplistic, so the payloadData are the attributes analytics expects and I generate a string of these parameters in the format the API expects, filtering out empty / null values:

    class Analytics {
      trackEvent(eventAction, eventValue) {
        if (!this.trackingId) {
          console.error('You need to set a trackingId, for example:');
          console.error('self.analytics.trackingId = \'UA-XXXXXXXX-X\';');
    
          // We want this to be a safe method, so avoid throwing Unless
          // It's absolutely necessary.
          return Promise.resolve();
        }
    
        if (!eventAction && !eventValue) {
          console.warn('sendAnalyticsEvent() called with no eventAction or ' +
          'eventValue.');
          return Promise.resolve();
        }
    
        return self.registration.pushManager.getSubscription()
        .then(subscription => {
          if (subscription === null) {
            // The user has not subscribed yet.
            throw new Error('No subscription currently available.');
          }
    
          const payloadData = {
            // GA Version Number
            v: 1,
            // Client ID
            cid: subscription.endpoint,
            // Tracking ID
            tid: this.trackingId,
            // Hit Type
            t: 'event',
            // Data Source
            ds: 'serviceworker',
            // Event Category
            ec: 'serviceworker',
            // Event Action
            ea: eventAction,
            // Event Value
            ev: eventValue
          };
    
          const payloadString = Object.keys(payloadData)
          .filter(analyticsKey => {
            return payloadData[analyticsKey];
          })
          .map(analyticsKey => {
            return `${analyticsKey}=` +
              encodeURIComponent(payloadData[analyticsKey]);
          })
          .join('&');
    
          return fetch('https://www.google-analytics.com/collect', {
            method: 'post',
            body: payloadString
          });
        })
        .then(response => {
          if (!response.ok) {
            return response.text()
            .then(responseText => {
              throw new Error(
                `Bad response from Google Analytics ` +
                `[${response.status}] ${responseText}`);
            });
          }
        })
        .catch(err => {
          console.warn('Unable to send the analytics event', err);
        });
      }
    }
    
    if (typeof self !== 'undefined') {
      self.analytics = new Analytics();
    }
    

    You can find all of this on: https://github.com/gauntface/simple-push-demo