This course has been updated! We now recommend you take the Build Progressive Web Apps (PWAs) from Scratch course.

Check out a free preview of the full Progressive Web Applications and Offline course:
The "Challenge 6: Solution" Lesson is part of the full, Progressive Web Applications and Offline course featured in this preview video. Here's what you'd learn in this lesson:

Steve walks through the solution to Challenge 6 and answers student questions with Mike's help.

Get Unlimited Access Now

Transcript from the "Challenge 6: Solution" Lesson

>> Steve Kinney: Our mission was to simply set up a service worker, right? We had some prior art in that glitch where we could see one setup but it was like still our mission was then to contextualize it in our application. So, we'll go through that together. And so the big question I have, does anyone remember where did we setup that other worker?

[00:00:24] The QR code worker, does anyone remember what file it was?
>> Steve Kinney: Where we actually registered it, where we imported it.
>> Speaker 2: Index.js.
>> Steve Kinney: Cool, there it is, hanging out. So, this is a worker and again, when we saw the super simple example, we saw that they just happened to be a file there, right?

[00:00:49] And then when we called service worker.register, we pulled that file. All of this like fanciness here is just web pack integrating with our buiid tools cuz it's a little like tedious to do otherwise. Like this looks tedious to me too, but you do it once and your built tools take care for the rest of it, right?

[00:01:09] So in this case, what we'll do is copy and paste that and we simply going to call it sw, because my font is large. You'll notice that when I'm on my own computer, I think I have plenty of space, and I like to give things long names. But when I'm using large fonts some of that changes, cool.

[00:01:30] So, we've pulled it in, we have access to it is in our build system, right, and there's kind of two schools of thought. We could put it like when the app mounts. Let's just do it in this file cuz we're here, unless Mike cuz you're gonna have to inherit this far from me.

[00:01:47] Do you care if I put an index? Mike doesn't care, Mike gives me permission, right? It's called teamwork, right? [LAUGH] Before you just flip things in the code based, your preferences, you talk to people. So we've pulled this in, let's make sure that we have that file. Looks like we do not, so we have to make it.

>> Steve Kinney: sw.js, and there it is. And we'll leave it blank for a second, that seems good for now. First thing we're gonna do is, ideally, it might happen to be that people who use the Safari web browser on their iOS devices and their Macs might visit this page.

[00:02:29] So what's the first thing that we should do in this case?
>> Speaker 2: Check and see.
>> Steve Kinney: Check to see, yeah. All right, let's not call, if we say navigator.service worker and then something, you are gonna get everyone's favorite error message in all of programming, undefined is not a function.

[00:02:45] Yes, I know modern Chrome doesn't show it like that anymore, but let me have this. Cool, so, we'll say if serviceworker in navigator.
>> Steve Kinney: So now we're in a special safe place that will only execute if serviceworker is supported.
>> Steve Kinney: And, we can go ahead and we'll say navigator.serviceworker,register.

>> Steve Kinney: Right, this is saying like, hey, I would like to register a service worker. Okay, what service worker would you like? sw.js, please. All right, so we've ever read this effectively will register it, right? Like we won't hear back, but if like we ran this code and went and looked in the tools, we would see that we have a service worker actually registered.

[00:03:40] Cool, but it does return our good friend the promise and the argument that's passed into that promise.
>> Steve Kinney: Selling is hard. Is a registration, right? This is the registration of the service worker in this case cuz things worked out. If it didn't work out, you wouldn't have a registration, you'd have an error.

[00:04:05] But here we have the service worker's registration. And, we could say something like,
>> Steve Kinney: Are there two Rs in register? Guess not, sweet.
>> Steve Kinney: And, that will be enough at this point, right, to get us going. And, so what we'll do is we'll go back over to the frontend groceries' page, and registration defined but never used, totally okay.

>> Steve Kinney: And let's go into application,
>> Steve Kinney: And we´ll go to service workers.
>> Steve Kinney: And we can see that service worker number 4453 is activated and running, neat. And you can see it was registered at 440. For those watching on the video, it is for 440, cool. And, there it is.

[00:04:58] And we're going to do anything because remember like, does anyone remember what's in sw.js right now?
>> Speaker 3: Nothing.
>> Steve Kinney: Nothing, right, so there's it is full proof and flawless. There's no way there are no bugs in this code at all whatsoever cuz it's an empty file. But it is registered and ready to go.

[00:05:16] You can see that it is registered and very fresh.
>> Steve Kinney: Few tasting notes here, right, one, it says registered again. It turns out that this promise will resolve even if it's re-registering. If there's one already installed, like we're still gonna count that as happy. Like, yes, you may be installed a new one, but like it worked out.

[00:05:40] Other interesting to know is that that was like literally the first thing that happened, right? Before we fetch anything else, anything along those lines, the registration happened, cool. All right, so this is so far so good.
>> Speaker 4: Why did it happen first?
>> Steve Kinney: Because it-
>> Speaker 4: Service worker is already running?

>> Steve Kinney: It pre-existed the page.
>> Steve Kinney: Right, and it happens at the very beginning. But even better, like just keep in mind that timeline for right now. Here we'll say, we have different events that could happen, right? We want to keep track of those counts, that's our mission right now.

[00:06:19] And so let's make a,
>> Steve Kinney: Counts. And we'll say like, keep track of installs, started at 0. We'll keep track of activations,
>> Steve Kinney: Start at 0, we keep track of fetches, all right, why not? You only live once.
>> Steve Kinney: Keep track of fetches, that number is gonna be fun.

[00:06:46] All right, cool. And so we can say, self which is the service worker and we can add event listener, and let's listen for install.
>> Steve Kinney: Cool, and we will say this one console log, we can say ++ counts.installs, right? So I will increment it by one every time that console logs it.

[00:07:16] So it will be 1, 2, 3 so on and so forth.
>> Steve Kinney: And we'll say, unactivate.
>> Steve Kinney: Say activations, and then,
>> Steve Kinney: Let's say fetches, cool.
>> Steve Kinney: So let's go ahead and we'll just refresh this page. That's neat, what I should probably do is prefix them with what actually happened.

>> Steve Kinney: So let's say, install,
>> Steve Kinney: Activate And the real fun one cuz we're gonna fetch every single one of these things, every single one of these items in here.
>> Steve Kinney: Cool.
>> Steve Kinney: And what we'll do now-
>> Speaker 4: So most of them are fetches.
>> Steve Kinney: Yeah, cuz we're fetching every single item on the page, every single one of those gifs.

[00:08:28] All right, I guess pngs of the tandoori naan and the whole wheat bread and the wheat, there's a lot of wheat happening right now. All of this wheat, each wheat is a fetch to get that picture of the wheat. So let's go in the application. Let's turn off update on reload cuz we're getting a fresh service worker on every reload

>> Steve Kinney: He said that's going up, we've 170, whoo hoo. We're up to 340, 425 and you know what I'm gonna just close this page and check out I'm just gonna stare at the beauty of this graph. I've closed all the pages, right? Like, memories gone, I know how the web works.

[00:09:10] You close the page, it's gone. Let's go back to my frontend browser.
>> Steve Kinney: It all stated memory, the service worker exists beyond a web page. So that exists and that's just an object in memory. We are gonna play with databases, we're gonna play with all sorts of like manually programmatically managing the cache, but we can see that we have this process.

[00:09:39] We could quit the Chrome and come back to it, right? But we left all the tabs we should have lost any local memory in that case. But this is a separate thread that still exists and it's ready to go. And we can hold onto that state.
>> Speaker 4: There's a complete check down effect that all.

>> Speaker 4: Shut down and restart.
>> Steve Kinney: At this point, Chrome is dead, right? We can turn off the computer. There will be things that we discuss tomorrow that obviously won't work, like notifications and pushes. But go to Chrome, let's find out. Let's go to the same version of Chrome open Canary, things will be a very different story?

>> Mike: I think that's Canary.
>> Steve Kinney: Yeah, go to this Chrome. Using other people's computers is the best. [SOUND] that one did start a six again I'll probably because, nope.
>> Mike: So I can shed a little light on the-
>> Steve Kinney: Shed some light on me Mike.
>> Mike: So why don't we keep things in in memory and JavaScript?

[00:10:49] How long would you expect that to live if I just had like, for x equals 65?
>> Speaker 4: The length of time here, there.
>> Mike: Absolutely. So as long as that scope survives, that memory is tied to that scope. In this case, that scope lives as long as that session within Chrome lives.

[00:11:11] And when you kill Chrome, when you close the app and it start it back up, the worker is registered. And so that script has been downloaded and Chrome has held on to it and it'll kinda off stir back again, but any of that volatile memory there has gone away.

>> Speaker 6: So it doesn't stabilize it or something to some?
>> Mike: It does not take that closure and persist it to memory and try to restore it. Just like in JavaScripit, right? We don't expect things that are just like hanging around to live beyond the lifetime of that session within our application, which is why you'd wanna process things to durable medium of some sort of like, index dB.

>> Steve Kinney: Seems like a good place to store stuff.
>> Mike: Yep, so it's the same service worker. It's gonna start up again but just think of it like, it's almost like you can have the same function but invoke it a second time and that's like a new scope that you're getting for that of the function that implication.

[00:12:12] So that number did not go up, we're still at 4456. It's because it's the same, if you think of it it's like a reference to that function it still that function. It's still a reference to the same service worker, so if you know what an AST is like the post code that is persistent to memory, but the scope of that particular use of the code that is not persistent.

[00:12:38] However it does live beyond lifetime of the tab, now it lives as long as we keep Chrome open. And when we kill Chrome, now we lose that and we can come back later on there. So you wouldn't wanna use this way of like storing data for a long time any more than you use it in your app to store data for a long time.

>> Steve Kinney: It's not dissimilar from thinking about like a server, all right? You can store some like variables, right? You're gonna lose them when the server dies. That's why we put stuff in databases, right?
>> Speaker 6: It's only a session store.
>> Mike: Only a few session store were in RAM.

>> Steve Kinney: Yeah, yeah.
>> Mike: But if you were like using Reddits for your session store, that's persistent.
>> Speaker 6: So in this case, like has it stored the entire school part only stores like can we control what we store like are we just ligand like everything that in-scopeofficeworker.js, it saves the whole thing.

>> Mike: So the AST is stored. So we take that text for that swjs file, parse it into JavaScript. We pay that price once and that data structure which is the tree, is stored. But that has nothing to do with using the service worker. It's almost like, it would be like storing a reference to a function.

[00:13:55] That is not the same thing as invoking that function and storing that scope of that particular invocation of the function to disk, does that make sense, the difference? So we can always start it up again but we've lost anything that was there in volatile memory, the last time we were hanging around.

[00:14:16] Though the usefulness of what Steve has set us up for here is to make the point that, it is volatile memory but its lifetime is longer than the tab. It's now like having Chrome open, that's your lifetime, right? And then it comes back.
>> Steve Kinney: That's a really good point.

[00:14:36] And then when we kinda reconvene tomorrow, the game plan is we're gonna look at the cache API, and the cache API allows us to actually have programmatic control of what the browser holds onto in terms of resources. We look at index DB which is a database-like level DB or Mongo where you can persist data in between launches of Chrome, right?

[00:14:56] So we have the tools necessary to do all of those things available from service worker. Service worker as I kind of open this section with is like the crown jewel that gives us, brings all these other technologies together for us. We can look at fetches and analyze them, right?

[00:15:14] We can programmatically work with caches. We can store stuff in the database. All of those will be exactly as we kind of expect them to, right? And it will exist outside of any given one of our tabs, right? And there's some other really cool things, I'm not gonna spoil the surprises yet.

[00:15:29] That by being in the background independent of any tab that you're able to do both. That was a really good point about the scope when you close Chrome as well.
>> Mike: So is there a way that we can poke around and scope other service providers?
>> Steve Kinney: Yeah, so check this out.

[00:15:44] Go to console, you've got the top. Whoa, what's down here? It's a service, sw.js my friend. Number 4456, we go back like all since like 4:44 PM today. And so now I am
>> Steve Kinney: Cool, and so if I say self,
>> Steve Kinney: There is my serviceWorker global scope,
>> Steve Kinney: And we could say, what do you want, what should we type in here?

>> Mike: Let's see counts, can we see counts?
>> Mike: No, no, maybe just counts.
>> Steve Kinney: Yeah, cuz we're in the context.
>> Steve Kinney: Yeah live coding fun times.
>> Mike: You know if we re-wrote that service worker so that we added counts to self, then we would be able to do that.

[00:16:33] But it's in like top level module scope basically at this point.
>> Steve Kinney: But you can, if there was code in here, so for instance let's actually give ourselves-
>> Mike: Once you un-register it programmatically in the console.
>> Steve Kinney: Yeah, I'm also gonna-
>> Speaker 4: So what were you trying to do there?

[00:16:54] Just get account of total accounts?
>> Mike: I was trying to get the object we've defined starting on line 1. However, when all of that, you don't want to immediately invoke the function expression is, it's like wraps. So it basically creates a private scope, effectively that's what's happened here.

[00:17:14] So we can't get at that, we would have to attach it to self.
>> Speaker 4: So we can't go to counts that fetches and count those.
>> Mike: Nope, but we could certainly attach it to self. Like the way we'd attach stuff to Window and old JavaScript days.
>> Steve Kinney: So in terms of debugging it, though, you can see I did while Mike was talking just a second ago, I created a function on self and globally available called hello world.

[00:17:43] And you can see that when we go into the developer tools, we can do it from the top level.
>> Mike: This is interesting.
>> Steve Kinney: Ooh, we got a friend. Our good friend, the active one, number 4456, has a buddy hanging out with us, right? 4459, who's installed and waiting to show up on the scene.

>> Steve Kinney: Which is super interesting. So we're on 4459, which is not the installed one. Go to 4456.
>> Mike: So this is the activated one?
>> Steve Kinney: Yeah.
>> Mike: Self.Hello world.
>> Steve Kinney: [CROSSTALK] But I can enter the installed one and debug that one as well where we had hello world, right?

[00:18:29] So if we go back over to application, you can see the 4459 is ready and waiting. Without this checked, I can refresh this. It's still ready and waiting. It's still ready and waiting, right? But it will not be able to take over until what?
>> Group: Till you stop the other one [CROSSTALK].

>> Steve Kinney: Yeah, until I either completely like close every tab currently accessing to it or check this fine button. Which will as the as the overlay which you actually can't see on that screen, but I can, you can see it just like very light when I look at it from here.

[00:19:01] Force update servers workaround page reload, and then if we click this, who's gonna take charge?
>> Group: 59.
>> Steve Kinney: 59.
>> Mike: We're getting a new one.
>> Steve Kinney: Or 62 because I changed the file, service workers are fun so-
>> Mike: Let's uncheck it and reload. So now we've got another one waiting.

>> Steve Kinney: Let's make a-
>> Speaker 4: So if you just hit stop there and there's one waiting, will that other one just automatically take over or does it have to reload?
>> Mike: We need a navigation event in order for something to be.
>> Speaker 4: So it has to reload, I gotcha.

>> Steve Kinney: Make some meaningless changes and head back.
>> Mike: Great, now if we go back to the console and get our active service worker set up, activated one. And we can do self.registration.unregister.
>> Mike: And it returns a promise but we don't care. Right now let's go and look back at our Application tab.

[00:20:16] So now look on the right, you see all of that ghosted out stuff. We can't click the Unregister link. It's been deleted, so we'll go through an exercise tomorrow where like one of my service worker war stories from LinkedIn. We need the ability if we push a bad service worker to tell it to take itself out.

[00:20:39] And to basically make like, progressively un-enhance the app so that we're back to normal.
>> Steve Kinney: Progressive de-hancement.
>> Mike: Progressive de-hancement, yeah. And so like this is a way we can programatically say like, there's APIs for saying, relinquish control and let the next one come in. But this one here was like, all right, we're done like that.

[00:21:01] The next one didn't even take over. This is like we don't have a service broker anymore. The next time it loads, if you go back to the console, you would see no counts. Because it's a fresh page for you again. So we've got install and activate and none of the fetch events were handled.

[00:21:19] Because the worker only became active after the app had booted, if that makes sense.
>> Speaker 6: When your newer one comes in, the older one's state is lost, right?
>> Mike: When a new service worker goes-
>> Speaker 6: That counter will be cleared.
>> Mike: And that scope self, that's a new self with each service worker.

[00:21:43] So the scope goes along with it.
>> Speaker 6: One more question. You mentioned that service workers can intercept requests. That's only mostly for the outbound requests from the browser to the network.
>> Mike: What kind of request were you thinking of?
>> Speaker 6: If you wanna ping a server or something else, like from browser, or if you're trying to receive something from the network without the browser being alive.

>> Mike: So from server to client?
>> Speaker 6: Yeah.
>> Mike: So we call that web push.
>> Speaker 6: Okay.
>> Mike: And yes, there is a way to intercept that. In fact, the browser doesn't have a way of receiving that without a service worker. Service worker provides us what we need in order to begin doing what you're talking about.

>> Speaker 6: And in this push, not push, whenever we are making that drop request, we are intercepting the fetch event. Do we get a callback when the response come in as well?
>> Mike: We can intercept the fetch request, and you have a couple of options there. You can allow it to proceed.

[00:22:53] One thing you can do as we've done here, you'll notice that we have something in that fetch event handler. But we're not really, we're clearly not intercepting a fetch request. We haven't interfered with the app's ability to go and get these images or the script or whatever. So you can leave things untouched, and there you don't really have a handle on when things will get resolved.

[00:23:17] You can grab the request off of that fetch event. And you can make your own call to fetch, at which point fetch, as Steve went through that, referred the promise. And so now, like often times what you wanna do is allow the request to proceed, but then when it comes back let's cache this.

[00:23:35] So that later if we need the same file again, I'm just gonna serve it to you without going out to the network. So you can let it parse through and do something after, or you can not let it go to the outside world and do whatever you wanna do.

>> Steve Kinney: This is actually the first topic we're gonna cover tomorrow morning.
>> Mike: Yeah, okay, so we need to describe one more API. Just gonna give everyone a crash course in the cache API, which is basically how we persist these requested response objects.
>> Steve Kinney: Yep.