Search code examples
angularservice-workerprogressive-web-appsangular-service-worker

Angular PWA force realod on android home screen app and browser


I have an angular pwa that checks for new versions and reload the new app code. It's working perfectly on any desktop browser but when i have a home screen shortcut made with chrome on my android device or in the mobile browser it's not updating its code when there is a new version. I have Chrome 73.0.3683.9 in the android device

I have added a timer with rxjs that checks for updates every 3 minutes (just for debugging) and reload if there are.

I'm adding the code here and hopefully someone could help me to understand why it's not working on mobile.

ngsw-config.json

{
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "app",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/favicon.ico",
          "/index.html",
          "/*.css",
          "/*.js"
        ]
      }
    },
    {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/assets/**"
        ]
      }
    }
  ]
}

updates.service.ts

@Injectable()
export class UpdatesService implements OnDestroy {

    private _unsb$ = new Subject();

    constructor(private _swUpdate: SwUpdate) {

        if (this._swUpdate.isEnabled) {

            // every 3 minutes
            timer(0, 1000 * 60 * 3)
                .pipe(takeUntil(this._unsb$))
                .subscribe(() => {
                    this._swUpdate.checkForUpdate()
                        .then(() => console.log('Finish checking for updates...'))
                })
        } else console.log('No service worker allowed');
    }

    CheckForUpdates() {
        this._swUpdate.available
            .pipe(takeUntil(this._unsb$))
            .subscribe(event => {
                this._swUpdate.activateUpdate()
                    .then(() => window.location.reload())
            });
    }

    ngOnDestroy() {
        this._unsb$.next();
        this._unsb$.complete();
    }
}

app.components.ts

export class AppComponent implements OnInit {

    constructor(private _updates: UpdatesService) { }

    ngOnInit(): void {
      this._updates.CheckForUpdates();
    }
  }

Solution

  • Ok so i mange to understand what the problem was, in mobile phone the service start polling via the timer befor that the app is stablized so from some reason it's not doing the check for updates correctly

     this._swUpdate.checkForUpdate()
                        .then(() => console.log('Finish checking for updates...'))
    

    So i added a check for if the app is stabilized and then start polling changes.

    updates.service.ts

      @Injectable()
      export class UpdatesService implements OnDestroy {
    
       private _unsb$ = new Subject();
    
       constructor(private _swUpdate: SwUpdate, appRef: ApplicationRef) {
           console.log('%c Update service is running...', 'color: green; font-weight: bold;');
    
           if (this._swUpdate.isEnabled) {
               console.log('%c Service worker enabled', 'color: orange; font-weight: bold;');
    
            // Allow the app to stabilize first, before starting polling for updates.
               const appIsStable$ = appRef.isStable.pipe(first(isStable => isStable === true));
               const everySixHours$ = interval(1000 * 60 * 60 * 6).pipe(startWith(0))
               const everySixHoursOnceAppIsStable$ = concat(appIsStable$, everySixHours$);
    
               everySixHoursOnceAppIsStable$
                   .pipe(takeUntil(this._unsb$))
                   .subscribe(() => {
                       console.log('%c Checks for updates...', 'color: blue; font-weight: bold;')
                       this._swUpdate.checkForUpdate()
                           .then(() => console.log('%c Finish checking for updates...', 'color: blue; font-weight: bold;'));
                   });
           } else console.log('%c No service worker allow', 'color: red; font-weight: bold;');
       }
    
       SubscribeForUpdates(): void {
           this._swUpdate.available
               .pipe(takeUntil(this._unsb$))
               .subscribe(event => {
                   console.log('current version is', event.current.hash);
                   console.log('available version is', event.available.hash);
                   this._swUpdate.activateUpdate()
                       .then(() => window.location.reload(true))
               });
       }
    
       ngOnDestroy(): void {
           this._unsb$.next();
           this._unsb$.complete();
       }
    }
    

    The ngsw-config.json and the app.component.ts stay the same. Hope it will help to somebody