What is the purpose of SwUpdate.activateUpdate() in Angular?

Some background on what the Angular ServiceWorker does under the hood:

  • There is always one version of the SW, but it can have multiple versions of the app.
  • It keeps a mapping of client IDs to app versions, so it knows which version to serve to each client.
  • When a new version is found, it downloads it and marks it as the latest version.
  • From that point onwards, new clients (e.g. new tabs) will be served that latest version, but existing clients (tabs) will still get their previously assigned version.

This behavior is a little different than how ServiceWorkers usually work. It is designed to allow serving the latest version to new clients, but not break existing ones (by serving them new content that is not designed to work with the older HTML/CSS/JS code they are using already).


Now, back to activeUpdate():

This basically tells the SW to assign the latest version to the client. Once the operation completes, new requests from the client will be served with the latest app version. This is not safe, therefore it is recommended to reload the page to ensure that all code comes from the new version.


Does simply reloading suffice?

This basically depends on how browsers assign Client.id on reloads. If the browser assigns a new ID to the tab on reload, then the SW will serve the latest version anyway, so swUpdate.activeUpdate() is not necessary. If, on the other hand, the browser keeps the same client ID, then the SW would serve the old version without swUpdate.activeUpdate().

Currently, Chrome seems to assign a new ID (and therefore activeUpdate() is unnecessary before reload). I couldn't find something conclusive in the SW spec, so I am not sure whether this is per the spec and whether all browsers do the same. (It is also possible that browsers used to behave differently in the past, making activeUpdate() necessary, but the spec/browsers have been updated since, making it redundant if you plan to reload.)


So, if you verify that your supported browsers behave as expected in that regard, then you don't need to call activeUpdate() before reloading.

Note that the documentation example is just a simplified illustration of the APIs that are available. In a real app, there are several ways to handle the update (depending on the app's requirements).

For example, you could:

  1. Activate the update immediatelly (if you are certain that your app can handle that) and not reload.
  2. Show a notification that an update is available and let the user click to reload (and thus update).
  3. Let the app know that an update is available and have it automatically reload on the next in-app navigation. (This is what we do in https://angular.io/ btw.)

Again, not all strategies are suitable for all types of apps, so choose the one that works best for you.


EDIT:
BTW, if you have suggestions on how the documentation could be improved, please open an issue (or better yet submit a pull request)!


From the github code, what I understand is, an activateUpdate() will do a postMessage() to the service worker thread to let it know about the new data and status and once the promise has been resolved(service worker thread knows about it), then reload the page.

https://github.com/angular/angular/blob/7.2.11/packages/service-worker/src/update.ts#L57-L64

activateUpdate(): Promise<void> {
    if (!this.sw.isEnabled) {
      return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
    }
    const statusNonce = this.sw.generateNonce();
    return this.sw.postMessageWithStatus('ACTIVATE_UPDATE', {statusNonce}, statusNonce);
}

https://github.com/angular/angular/blob/master/packages/service-worker/src/low_level.ts#LC105

postMessageWithStatus(type: string, payload: Object, nonce: number): Promise<void> {
    const waitForStatus = this.waitForStatus(nonce);
    const postMessage = this.postMessage(type, payload);
    return Promise.all([waitForStatus, postMessage]).then(() => undefined);
}