Sharing the same code between PWA and Native (iOS/Android) is a practice we have been using on our Ionic apps. An essencial service for many projects is Google Analytics (GA). We did a deep search to find the best option for our scenario. There was a main solution out there.

Ionic Native Google Analytics Plugin

It’s easy to add to your project, there are a lot of good tutorials like this. If you use just for native apps, it can be the best option. Otherwise for PWA the plugin just works when the app is compiled for browser platform and in this situation it produces a bigger webapp than necessary (6mb more for just one feature). Besides the size, browser platform will be depreceated according this comment of an Ionic Developer Team member.

UPDATED: July 25th, 2019
Ionic PWA Analytics

How we did using raw GA

I’ve used this project for tests :) feel free to clone/fork/contribute

index.html - added GA code snippet

...
<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');
</script>
...

src/app/analytics.service.ts

Install @types/google.analytics in order to allow works with js library in typescript.

$ npm install --save @types/google.analytics

Create a provider to work with GA.

$ ng g module shared --spec=false 
$ ng g service analytics shared/services/analytics
import { Injectable } from '@angular/core';
declare var ga;

@Injectable({
  providedIn: 'root'
})
export class AnalyticsService {

  constructor() { }

  setTracker(tracker) {
    if ( !localStorage.getItem('ga:clientId') ) {
      localStorage.setItem( 'ga:clientId', tracker.get('clientId') );
    }
  }

  startTrackerWithId(id) {
    ga('create', {
      storage: 'none',    
      trackingId: id,
      clientId: localStorage.getItem('ga:clientId')
    });    
    ga('set', 'checkProtocolTask', null);
    ga('set', 'transportUrl', 'https://www.google-analytics.com/collect');
    ga(this.setTracker);
  }

  trackView(pageUrl: string, screenName: string) {
    ga('set', {
      page: pageUrl,
      title: screenName
    });
    ga('send', 'pageview');
  }

  trackEvent(category, action, label?, value?) {
    ga('send', 'event', {
      eventCategory: category,
      eventLabel: label,
      eventAction: action,
      eventValue: value
    });
  }
}

How to start tracking by id was inspired by this article who explains how solve some problems in native client.

src/app/app.module.ts

...
import { AnalyticsService } from './analytics.service';
...
@NgModule({
 ...
  providers: [
    ...
    AnalyticsService,
    ...
  ]
})

src/app/app.component.ts

...
import { AnalyticsService } from './analytics.service';
import { Router, NavigationStart } from '@angular/router';
import { Title } from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html'
})
export class AppComponent {
  constructor(
    ...
    private analyticsService: AnalyticsService,
    public router: Router,
    private title: Title,
  ) {
    this.initializeApp();
  }

  initializeApp() {
    //Start track passing Tracker Id
    this.analyticsService.startTrackerWithId('XX-XXXXXXXX-X');
    ...
    this.router.events
    .subscribe(event => {
      //observe router and when it start navigation it will track the view
      if (event instanceof NavigationStart) {
        let title = this.title.getTitle();
        //get title if it was sent on state
        if (this.router.getCurrentNavigation().extras.state) {
          title = this.router.getCurrentNavigation().extras.state.title;
        }
        //pass url and page title 
        this.analyticsService.trackView(event.url, title);
      }
    });
  }
}

Track Screen title

Sometimes besides the URL, you need to show more meaningful infos of your app on anlytics. In our case it’s easier to understand Awesome new! than /item/detail/123456 when you are reading analytics data. So on items list, for each link we pass the title of item in state attribute on template.

<a routerLink="/items/edit/123456" [state]="{ title: 'Awesome new!' }" routerDirection="root">
  Awesome new!
</a>

example.ts - tracking some event

import { AnalyticsService } from './analytics.service';
export class ExamplePage {	
  constructor(
    public analytics: AnalyticsService,
  ) {}
  exampleEvent() {
    this.analytics.trackEvent('User', 'Create User', 'Daniel');
  }
}

Furthermore


Daniel Antonio Conte

Life, Universe, Everything

Follow me