If you're building an application that works offline, then understanding how users are interacting with your app when they don't have connectivity is crucial to optimizing that experience.
Analytics providers like Google Analytics require a network connection to send data to their servers, which means if connectivity is unavailable, those requests will fail and those interactions will be missing from your analytics reports. It'll be like they never happened.
Workbox Google Analytics solves this problem for Google Analytics users by leveraging Service Worker's ability to detect failed requests.
Google Analytics receives all data via HTTP requests to the Measurement Protocol, which means a Service Worker script can add a fetch handler to detect failed requests sent to the Measurement Protocol. It can store these requests in IndexedDB and then retry them later once connectivity is restored.
Workbox Google Analytics does exactly this. It also adds fetch
handlers to cache the
analytics.js and
gtag.js
scripts, so they can also be run offline. Lastly, when failed requests are
retried, Workbox Google Analytics also automatically sets (or updates) the
qt
in the request payload to ensure timestamps in Google Analytics reflect the
time of the original user interaction.
Enabling Workbox Google Analytics
To enable Workbox Google Analytics, call the initialize()
method:
import * as googleAnalytics from 'workbox-google-analytics';
googleAnalytics.initialize();
This is the only code that's required to queue and retry failed requests to Google Analytics, and it's the simplest way to get Google Analytics working offline.
However, if using only the code above, the retried requests are indistinguishable from requests that succeed on the first try. This means you'll receive all the interaction data from offline users, but you won't be able to tell which interactions occurred while the user was offline.
To address this concern, you can use one of the configuration options described below to modify or annotate the data that gets sent in the retried request.
Modifying what data gets sent
If you want to be able to differentiate retried requests from non-retried
requests, you can specify either the parameterOverrides
or hitFilter
configuration options.
These options let you modify the
Measurement Protocol parameters
that get sent in the retried request. The parameterOverrides
option
should be used when you want to set the same value for a particular
parameter for every retried request. The hitFilter
option should be used
in cases where the value of a particular parameter needs to be computed at
runtime or derived from the value of another parameter.
The examples below show how you'd use both options.
Examples
Using a custom dimension to track online vs. offline interactions
Google Analytics does not have a built-in dimension for online vs. offline interactions. However, you can create your own dimension for exactly this purpose using a feature called custom dimensions.
To track requests that were replayed by the service worker using a custom dimension with Workbox Google Analytics, follow these steps:
- Create a new custom dimension in Google Analytics. Give it a name like "Network Status" and set its scope to "hit" (since any interaction can be offline).
Take note of the index assigned for the newly created dimension and pass that as the parameter name to the
parameterOverrides
configuration option in your Workbox Google Analytics code.For example, if this is your first custom dimension, its index would be
1
, and the parameter name would becd1
(if the index were8
it would becd8
):import * as googleAnalytics from 'workbox-google-analytics'; googleAnalytics.initialize({ parameterOverrides: { cd1: 'offline', }, });
(Optional) Since values in
parameterOverrides
are only applied to retried ("offline") requests, you may also want to set a default value of "online" for all other requests. While this isn't strictly necessary, it'll make your reports easier to read.
For example, if you used the default analytics.js tracking snippet to install Google Analytics, you could add the linega('set', 'dimension1', 'online')
to use a default value of'online'
for your "Network Status" custom dimension for all requests not replayed by the service worker.<script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-XXXXX-Y', 'auto'); // Set default value of custom dimension 1 to 'online' ga('set', 'dimension1', 'online'); ga('send', 'pageview'); </script>
Using a custom metric to track time requests spent in the queue
If you were curious to know how much time had passed between when an offline
interaction took place and when connectivity was restored and the request was
successfully retried, you could track this using a
custom metric and
the hitFilter
configuration option:
- Create a new custom metric in Google Analytics. Give it a name like "Offline Queue Time", set its scope to "hit", and set its formatting type to "Time" (in seconds).
Use the
hitFilter
option to get the value of theqt
param and divide it by 1000 (to convert it to seconds). Then set that value as a param with the index of the newly created metric. If this is your first custom metric, the parameter name would be'cm1'
:import * as googleAnalytics from 'workbox-google-analytics'; googleAnalytics.initialize({ hitFilter: (params) => { const queueTimeInSeconds = Math.round(params.get('qt') / 1000); params.set('cm1', queueTimeInSeconds); }, });
Testing Workbox Google Analytics
As Workbox Google Analytics uses Background Sync to replay events, it can be unintuitive to test. Read more at Testing Workbox Background Sync.
Types
GoogleAnalyticsInitializeOptions
Properties
-
cacheName
string optional
-
parameterOverrides
object optional
-
hitFilter
void optional
The
hitFilter
function looks like:(params: URLSearchParams) => {...}
-
params
URLSearchParams
-
Methods
initialize()
workbox-google-analytics.initialize(
options?: GoogleAnalyticsInitializeOptions,
)
Parameters
-
options
GoogleAnalyticsInitializeOptions optional