Hey! ๐๐ป
This time we are going to talk about updating our PWA properly. As we know, a PWA should work with cached files, and sometimes if we update those files, there is no way to let the PWA update the cache. This time, we are going to implement Angular's SwUpdate service.
What
SwUpdate is the service that notifies us that there is a new version of the application. It can do four things:
- Notify us of available updates.
- Notify us when an update is activated. This is when the service worker shows us the latest version of the app.
- Asks the service worker to check for new updates.
- Asks the service worker to activate the latest version of the app.
Why
This is pretty useful when we are continuously improving our application and want our users to have always the latest version of the app.
In the project I'm currently working on, we've just launched our first stable version and it's being used by hundreds of users. Before implementing this, we had to request our users to clean the cache and reload the app. That adds an unnecessary burden for us and the users. Also, not all the users may get the message and it will lead us to fragmentation in our userbase where some of them work with the latest version of the app, but others work with an older version (in the best case scenario)
How
Let's go through the process of implementing the auto-update feature to our app.
Is it already a PWA?
First of all, we need to be certain that we have our app set up as a PWA. To do that we need to check the following in our app:
- src/manifest.webmanifest exists.
- ngsw-config.json exists.
- the property: projects.{project-name}.architect.build.configurations.production... exists and is set to true.
- app.module.ts has imported ServiceWorkerModule.
Surely, there must be more indicators, but with those four we can be pretty certain that our app is already a PWA.
Implement SwUpdate
When we import ServiceWorkerModule there are a couple of services at our disposal. One of them is SwUpdate which we will use to check for updates.
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
constructor(private swUpdate: SwUpdate) {
}
ngOnInit() {
if (this.swUpdate.isEnabled) {
this.swUpdate.available.subscribe(() => {
if(confirm("New version available. Load New Version?")) {
window.location.reload();
}
});
}
}
}
Let's go line by line and see how this works:
if (this.swUpdate.isEnabled) {
Here we check if the Service Worker is enabled (it may not be supported by the browser), if it is, it will continue the execution of whatever is inside.
this.swUpdate.available.subscribe(() => {
...
});
Here we subscribe to the swUpdate.available observable. It emits an UpdateActivatedEvent whenever a new update is detected by the service worker. For each new update, it executes whatever is inside.
if(confirm("New version available. Load New Version?")) {
window.location.reload();
}
Here we inform the user that there is a new version and ask it if it wants to reload the page with the new version.
Now, every time we upload a new version, those users will be notified and "update" their app on reload.
Pro-tip: integrate GitHub Actions
To have a smoother deployment, I suggest integrating GitHub Actions to deploy automatically every time you merge your new code into master. And now every time you merge new features or fixes into master, it will be deployed automatically and then all your users will detect and update their app almost without their intervention (you can avoid using "confirm()" and reload the app automatically)
For more details please check my post on GitHub Actions: https://myowntake.hashnode.dev/my-take-on-github-actions
Let's wrap it there. As always, I hope you can take anything useful from this and I'd like to know your thoughts.