Angular Service Worker SwUpdate.available not triggered

23,788

Solution 1

You will probably need to tell the service worker to check the server for updates, I usually use a service for this:

export class UpdateService {

  constructor(public updates: SwUpdate) {
    if (updates.isEnabled) {
      interval(6 * 60 * 60).subscribe(() => updates.checkForUpdate()
        .then(() => console.log('checking for updates')));
    }
  }

  public checkForUpdates(): void {
    this.updates.available.subscribe(event => this.promptUser());
  }

  private promptUser(): void {
    console.log('updating to new version');
    this.updates.activateUpdate().then(() => document.location.reload()); 
  }

In your app-component.ts:

  constructor(private sw: UpdateService) {
    // check the service worker for updates
    this.sw.checkForUpdates();
  }

For whatever reason, Angular sometimes does not register the service worker properly. So you can modify `main.ts` :

Replace:

platformBrowserDynamic().bootstrapModule(AppModule);

With:

platformBrowserDynamic().bootstrapModule(AppModule).then(() => {
  if ('serviceWorker' in navigator && environment.production) {
    navigator.serviceWorker.register('ngsw-worker.js');
  }
}).catch(err => console.log(err));

Solution 2

I found the following info in the angular docs: https://angular.io/guide/service-worker-devops#debugging-the-angular-service-worker

Summary: There's a useful endpoint on angular sites that shows the service worker state:

http://site-url/ngsw/state

Append /ngsw/state to your sites address. If there is something wrong with the service it should show there.

Solution 3

After every other solution on the stack didn't work, I've started debugging angular's ngsw-worker.js script. I've traced the updating logic and ended up in the "PrefetchAssetGroup" method. I've inserted logging every time method got called and then I realized that it is constantly trying to cache my favicon.ico. I've checked the location of the favicon.ico in my ngsw.json and realized that favicon is not located there. Once I've placed the icon on that location everything started working fine.

Share:
23,788
Admin
Author by

Admin

Updated on January 12, 2021

Comments

  • Admin
    Admin over 3 years

    I'm having a hard time integrating angulars service worker into my application. I followed the guide and it works so far. I can create a shortcut on my homescreen and launch into my app. The problem is that my app somehow doesn't update. If I change the name of a button, build the app and put it onto my server the app still shows the old version until I hit F5 (restarting the app doesn't help either).

    I tried to put the following code into my ngOnInot of my app but it didn't help

    ngOnInit() {
    if (this._SwUpdate.isEnabled) {
    
      setInterval( () => {
        this._SwUpdate.checkForUpdate().then(() => console.log('checking for updates'));
      }, this.updateInterval);
    
      this._SwUpdate.available.subscribe(() => {
    
        console.log('update found');
    
        this._SwUpdate.activateUpdate().then(() => {
          console.log('updated');
          window.location.reload();
        });
    
      });
    
    }
    

    }

    The app is running on my apache2 linux machine. Is my apache caching something or why doesn't my app realize that there is a new version?

    Thanks in advance for your help :)

    Edit:

    My ngsw-config.json

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

    Edit 2:

    It works if I run the app local using "http-server" but when I copy the files over to my apache it doesn't detect the update. In the networking tab I can see that the interval works, the app gets a new "ngsw.json" from the server every 3 seconds. If I update my app I can see that there are new hash values inside of the response for "ngsw.json". After that the browser loads the new "index.html" and "main.***.js" from my server but the app doesn't apply the new version. According to my code it should say "update found" but nothing happens.

    • Pankaj Parkar
      Pankaj Parkar about 4 years
      Angular docs are the best resource for this, pleaser refer this page
  • Admin
    Admin about 6 years
    I did as you said (kept an empty tab open and restarted the http-server after making changes) but the service worker didn't update to the new version, I had to completely close the browser and start it again to get latest version. Is there maybe something wrong with my ngsw-config.json?
  • Admin
    Admin about 6 years
    It seems like the promise returned from checkForUpdate() is never fullfilled, the interval works but the 'checking for updates' never appears in my console.
  • Michael Doye
    Michael Doye about 6 years
    Are you 100% sure the service worker has been properly registered?
  • Michael Doye
    Michael Doye about 6 years
    @MadMurl0c check the updated answer, I modified the promptUser method and added something to change in main.ts and here is an example in a demo project
  • Admin
    Admin about 6 years
    Still doesn't work, the SwUpdate.available.subscribe never gets triggered but I don't know why. I put a console.log in it but I can never see it. If I build my app and place it on my server the service worker doesn't react to it at all
  • Chellappan வ
    Chellappan வ about 6 years
    sorry can you add this._swUpdate.checkForUpdate() inside your if condtion
  • Admin
    Admin about 6 years
    The service worker seems to work according to dev tools and after resetting my chrome data I get the "checking for updates" in my console, the app doesn't update though, it seems like he's checking for updates but never realizing that there is an updated version.. Could my apache somehow cache the version number so that my service worker doesn't know that there is a new one?
  • Admin
    Admin about 6 years
    I'm using an interval which checks for updates every 10sec but it doesn't react to the new version (I updated my code sample)
  • Admin
    Admin about 6 years
    Thanks, now the service worker gets started correctly and not only on every ~3rd try, but it still doesn't update although it atleast checks for updates now
  • Michael Doye
    Michael Doye about 6 years
    did you see the update to promptUser which now activates the update using this.updates.activateUpdate().then(() => document.location.reload()));
  • Admin
    Admin about 6 years
    I'm not sure if I'm allowed to share the whole code because it's an internal project. Which files could I provide to help?
  • Admin
    Admin about 6 years
    Yes I saw that and I integrated it into my code (updated the example in my first post) but this part never gets called, I put a console.log in there just to make sure but it doesn't detect the update
  • Chellappan வ
    Chellappan வ about 6 years
    did you added this.swUpdate.checkForUpdate() inside your ngOnInit lifecycle hook?
  • Admin
    Admin about 6 years
    How can I find out if my Apache is the problem? Unfortunately I can't put my project onto a public server
  • Michael Doye
    Michael Doye about 6 years
    Not 100% sure, but this answer might be useful - stackoverflow.com/a/34161385/3055401
  • Admin
    Admin about 6 years
    I did but it didn't change anything. If it runs the checkForUpdate() method I can see that it request 3 files in the network tab "ngsw.json?ngws-cache-burst=...., config.php, config.php?ngsw-cache-burst=..." I don't know why my config.php is in there but that's all what happens
  • Admin
    Admin about 6 years
    Just that I understand SwUpdate right... I go into my app, write "hello world" in any component, ng build --prod my app, copy it to my apache2 and the service worker should refresh it... right?
  • Michael Doye
    Michael Doye about 6 years
    That's right yes, check the debug log at yourdomain.com/ngsw/state and see if there is anything in the Task queue
  • Admin
    Admin about 6 years
    Great news.. It now works with http-server! So my apache must be the problem, but what could it be?
  • Admin
    Admin about 6 years
    The app gets the new ngsw.json file and theres a new hash for main.js in it but the service worker doesn't load the new version of main.js... On localhost it works fine
  • Admin
    Admin about 6 years
    After completely disabling cache he loads the new main.js but it doens't trigger the update subscription
  • Admin
    Admin almost 6 years
    I just completely reseted my test system and set up a new apache server.. After applying your fix in the main.ts everything works now, thanks for helping me out
  • Vytautas Pranskunas
    Vytautas Pranskunas almost 6 years
    Service worker is not registered sometimes only because you have setInterval and app is never stable from the beginning because it is triggering ngZOne change detection. To avoid this run all intervals outside angular.
  • ishandutta2007
    ishandutta2007 about 5 years
    ERROR in ..: error TS2304: Cannot find name 'interval'.
  • ishandutta2007
    ishandutta2007 about 5 years
    where is navigator defined.
  • Kim
    Kim about 5 years
    window.navigator @ishandutta2007
  • Kim
    Kim about 5 years
    @VytautasPranskunas can you elaborate on "run all intervals outside angular"?
  • ishandutta2007
    ishandutta2007 about 5 years
    I couldn't make it work. created a new question with the issues i am geting from this code. @Yeswhen you can take a peek too stackoverflow.com/questions/55797611/…
  • Angad
    Angad about 5 years
    I am having the same issue. Can you tell me what did you do to fix this? Do I need to disable caching on server? It works for me on http-server but when I host it on azure box the available event is never triggered.
  • Kim
    Kim about 5 years
    Yes, the server should not cache ngsw-worker.js. Did you register the service worker in main.ts like in the answer above? You can try register it by yourself in the console by writing navigator.serviceWorker.register('ngsw-worker.js'), maybe the path is different in your azure box @Angad
  • Bhaumik Thakkar
    Bhaumik Thakkar about 5 years
    @MichaelDoye can you please tell me the proper testing scenario(basically how can I verify that it's working properly or not?) for service worker in local mode.
  • Michael Doye
    Michael Doye about 5 years
    @BhaumikThakkar please see the docs here: angular.io/guide/…
  • Abner
    Abner almost 5 years
    @MichaelDoye Is this solution still valid and recommended for Angular V8? I notice after an update my PWA in IOS did not reload the page, meaning it probably didn't trigger the activateUpdate()
  • Tahseen Quraishi
    Tahseen Quraishi over 4 years
    It worked for me. I did same as @MichaelDoye has suggested.
  • Jimmy Kane
    Jimmy Kane about 4 years
    I dont understand this solution to be honest. Why the setInterval? Could you be a bit more explanatory in your answer? For me it works as it should at the time of writing but are having other problems so I do like the answer but don't understand the flow.
  • Michael Doye
    Michael Doye about 4 years
    @JimmyKane the interval is there because we need to poll the server to check for updates, this is described in the documentation (although their implementation has changed a bit)
  • iwantcheeseburger
    iwantcheeseburger almost 4 years
    Hi @MichaelDoye, I've tried your solution and it works fine in my case, but is there any alternative where if it check for an update then found one, that updates.checkForUpdate method will be stopped? because it is calling every 20 secs and I dont want to overload the server call. Thanks.
  • Aaron Turkel
    Aaron Turkel almost 3 years
    @AlvinYanson change the line with ` interval(6 * 60 * 60)` to a normal interval declaration with the function inside and then clear it when the update happens. Or if you are referring to the update subscription you can unsubscribe on update aswell.