Build Progressive Web Apps (PWAs) from Scratch

Updating Cache Resources

Maximiliano Firtman

Maximiliano Firtman

Independent Consultant
Build Progressive Web Apps (PWAs) from Scratch

Check out a free preview of the full Build Progressive Web Apps (PWAs) from Scratch course

The "Updating Cache Resources" Lesson is part of the full, Build Progressive Web Apps (PWAs) from Scratch course featured in this preview video. Here's what you'd learn in this lesson:

Maximiliano demonstrates how to update and store cached resources on the device using cache first and network first strategies. The app can then bypass the service worker in a cache first strategy to force an update or always go to the network and render the newly cached information on the next reload.


Transcript from the "Updating Cache Resources" Lesson

>> So we have a question about updating the cache or how to. Or if we should check if the cache is updated. And this is a big topic, the cache updated. Because the first problem that we have, because we are serving from the cache, some of you are realizing that we may have a problem.

Because we are always serving from an old version of the file. The file that we register when we install the service worker, that maybe it was a year ago. So the problem, the challenge that we have here, is that there is no way in the HTTP protocol, or simple way, to know which assets were changed server side.

But we don't have a way to that. So, we need to define our own algorithm, to detect or to know that the file has been updated in the server, so we can update that client side. There are a couple of solutions and best practices, and we're going to see some,okay?

But that's the first response. Or the fourth point is that, yeah, it depends, okay? And it's not so simple. But before I tell you, moving on on that part, here is a part we're actually following the code. Take some time. So that's why I want to show out the snippets that I have here.

It's not a big deal. But in case you want to copy and paste, or something like that, it will be better to follow this. For example, here I have the cache first strategy. Okay, so it's similar to the one that I have, but without that fake URL designing some noise, in case we want to understand the code.

So, I'm going to replace this with this snippet that is just a simple version of that, okay? So, we are actually matching the request to see if it's there. If we do have a response, we return that. If not, we return a fetch. So it's the same code.

And we are returning these to respond with, actually. Okay, so this is cash first. So, let's write this algorithm first, and then we move to another one. So I'm going to refresh first. Everything is okay. Everything seems fine. Yep, now I will try offline. So, now is the time where we're going to cross fingers with offline clicked, and I'm going to reload.

if I reload, now, my PWA works. It is still working while offline. If you don't believe this is true, we can also turn off the server. So now, we don't have the server, and I refresh. Now, I only need update and reload. But I need offline. I refresh, and the app is there.

So we are offline, we are officially offline. Okay, just because I'm doing this. So now, our app is offline capable. So if we go to manifest, now we can see we do not have any warnings. Which means, now it's officially installable as a PWA. So now on desktop, I can go to the menu, and I can see install Codepad Masters, the best PWA in town.

I can click there, I can install the app, and now I have a real PWA running on my desktop. And it also works offline. But we are still missing one piece. If I add some node here, and then I refresh, I'm loosing that content. And to be offline, we are trying to save the data.

That simple. That has nothing to do with the PWA platform. That's just the web. You can use index TV, or local storage, to save the data In my case, I will just use local storage. It's actually pretty simple. I'm going to app.js. And first, every time we need to save the data, that is for example here, this is when we are pushing a new note, we can call save.

And also, when we are deleting, we can call save as well. That save function does not exist, so we need to actually create that one. Function save, by going to take local storage. We're going to set an item, this will create a new entry. With one particular key, we can call this notes.

And we can only store on a string. So we need to astringify our array of notes into JSON format. So actually, it's going to be string. Do you see how we save? And then, when the page loads in our DOM content loaded event, before rendering a note, I can check if I have some save notes.

And then, I will retrieve them. So, that's easy as well. I will check if in the local storage, we have something for the item notes. And if we do, we are going to save in our collection JSON.parse of local storage, get item notes. So now, let's go back to Chrome and see if it now works completely offline.

We can refresh or reload. Right click reload to get the latest version. And let's see. If it's saving. If I click and reload, you can see it's not. And you say, what's going on? Well, I am not using dev tools here. So, which means, the reload system that reloads the ServiceWorker, it's not actually working.

And I didn't change the ServiceWorker file. So, now that I have installed a version, I always have the old version. So I always have the old version of app.js. That's my problem. So how can I actually do this? How can I force an update? Well, we can see that cache first has some limitations.

So, we have a problem, like when you drop the these app.js. Let me show you the same with another example. We can change one color. For example, instead of the title back, we can change these to red, okay? So, I'm changing that color to red, and I'm coming here right click Reload, it's all red.

Right click Reload, it's not red, why? Because I'm using the cached version always. So, we have a problem. Can I shift Reload? Well, not actually from the PWA. If I'm in the browser, I can do Shift Reload. In fact, let's do that. Let's close the PWA, and open this in the browser.

I can close that, where I can use the menu opening Chrome. And now we are back into Chrome. Again, I refresh nothing happens. I shift refresh, and I see red. Because now, I'm bypassing the service worker, and I'm going directly to the server. But if I go back and refresh normally, I'm back in the old version.

So we have a problem, okay? So we shift reload, this will probably save the data. Shift reload, it works. I'm shift reloading, I still have the data. But if I reload normally, I don't have the data. So, it's like we have two versions of the app running at the same time.

So that's because, cache first it's actually limited. And we need another way to update the app. One simple way that you have is that, you can go to a ServiceWorker, okay? And then change something, such as one space. Doesn't make any sense. More than changing a space, you can change a character, such as the version number.

So, every time there is a change in one bite, because the browser is doing the byte by byte comparison in the ServiceWorker, it will trigger an update. The parameters of the update will actually install the assets again. Okay, so let's say, I'm coming here and going to reload.

I may still do in the old version, but if I reload again without shift normal reload, I'm getting the new version. And now, I will always have the new version. Let's do that again. If we don't like that red, and we wanna go back to the previous color, we go back.

We save the file. And we also go and change the version of a ServiceWorker, or a timestamp, or any character. So now, if I go back to Chrome, I reload twice, and I'm getting the new version. Okay, so this is one solution to the update problem. If every time you make a change in one asset, you go and update something in the service worker, it can be a comment, then the user will actually get the whole package again.

That's one way, and now everything works offline. Even deleting data, or saving new notes. Another solution is to change the algorithm. If you look at the snippets that we have here, in this case, it goes to the network. And only if that operation fails, it goes to the cache.

But on network first, we also have the problem of updating the assets, because we are not updating the assets. So when I show you the other one, that is like larger, so I'm going to copy this. And we can replace the fetch. Or I can comment this fetch, and just replace with this.

This is called stale while revaluate. Let's first analyze the code, and then we see it in action. It's gonna respond with cache is much. So, it's actually similar to the cache first. Okay, so it goes to the cache, and see if the request is there. Then, it will return that cached response.

In fact, if you want, we can change the name here too. So it's going to be more clear. So it's going to return the cache respond. And there is an order here. So if it's undefined, so if we do not have a cache respond, I'm returning with a fetch.

So it looks similar to cache first, with one particular addition. We are always going to the network, even if it's in the cache. So, here's the deal. You're asking me for a PNG, so I go to the cache, okay? And no matter the result, I'm also going to the network to bring that PNG file.

If I do have that file in the cache, I will give you the cache file. But I'm also going to the network, I'm bringing that PNG from the network, and I'm updating the cache. Okay, so this is what this code is doing. It's updating the cache always. Every time you request a file, it returns the file from the cache if it's there.

But it's updating the file in the background. So, next time someone is requesting that file, it will be an updated version. Does it make sense? Let's see it in action. So, first because I have changed a ServiceWorker, it's a good idea to maybe open the Service Workers panel, and check that we have updated reload.

I'm going to refresh so I know for sure that I'm in the latest version of the Service Worker. So now, I'm going to untick update and reload. So now, I'm a normal user, okay? So I'm going to change one CSS. So for example, if we wanna change this color with pink.

Now going back here, what I knew is to get the pink. So I'm going to reload ones, I'm gonna stealing yellow, and then I'm finally getting the new color. So with the stale while revaluate, you can change assets individually. And then, you don't need to change the service worker.

So, I don't need to go and change one bit the ServiceWorker. It is still a double reload, which is expectable. Why? Think about the native app, or native apps, you are always having an old version because it's installed in your device. So here, we have the same idea.

So, we are always loading the version that we have. Then in the background, we can realize there is a new version and update the assets. This can get more complicated with your own algorithm. And some libraries, such as workbooks, will actually help you in improving this user experience.

But before moving on, let's go back to our original color. And now we are here. So, when we are serving file from the cache, we have different strategies. Cache first, network first, or stale while revalidate. And we can do a mix. It's up to you, it's up to your own algorithm, and also it can defer per URL.

Updating the resources, it's actually a challenge. Because files are served in the client, and updating files in the server won't trigger any automatic change in the client. So we need to define, and code, and update algorithm, okay? So, if you have a build process, such as you're doing a react application, sometimes we hash resources.

Or we version resources, so we can create the manifest file separated from your web manifest and assets manifest that can define all the assets with a version number. And then, the service worker can actually see and compare the assets, and let's say, upgrade all the assets that are old in the cash.

Developer is in full control of how to cache and serve the resources of a PWA. And also, how to manage API calls. Because if you want, you can also cache API calls. Or you can synthesize responses. So, you can have some Graph QL API, but completely client signed over IndexDB.

She has an example. Updating the app doesn't require full reinstallation as the native apps. We can just replace the updated files silently, or with a user's notification. And that's actually one of the challenges of PWA development, how to do that. To synthesize the service worker solution, before actually seeing our app installed on android, let's talk about the life cycle again.

So, the first time you load a PWA, you don't have a ServiceWorker. The ServiceWorker is then passed, installed, and it's waiting. Waiting for what? You will see in a minute. After waiting, it goes to activated, and is now in full control of the origins of the scope. And then, it's not executing code, it's waiting for an event.

It can be a fetch, a push message, or a message that is received by a client. In that case, the Service Worker goes to running. So now, it's running. And after some seconds of inactivity, but in Chromium is 40. On Safari it's less than 40, around 15. It goes against your Idle.

And at one point, it can be stopped or terminated. And it can go back to running automatically when another event happens. That's how it works. And every time the service worker appears again, it might not be the same instance. So we don't wanna use, for example, global variables in the service workers scope.

Because maybe after 40 seconds, they are gone. Yeah, maybe you're back, but it's a new instance. So nothing is the same. So, the question has to do that, if we have any ability to increase the idle time of a service worker, the answer is no. In fact, we don't even know that time we are not in control of that.

The Service Worker is in control. And even, when we are asking the Service Worker to wait for us with wait until, the browser can always kill us at any time. It can kill our process if the browser thinks that we are doing something wrong with the CPU, for example.

Remember that we don't need users permission for installing a Service Worker. So that's why the browser is actually really careful about the permission that it's granting us.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now