What is the big deal about Progressive Web Applications (PWAs) and why are they trending nowadays? Here is one explanation by Google:
“It [PWA] loads quickly, even on flaky networks, sends relevant push notifications, has an icon on the home screen, and loads as a top-level, full screen experience.”
The number of users accessing web applications from a mobile device is bigger than those using desktop. And it’s increasing. If you want your web app to reach a bigger audience, while increasing the usability and experience for your users, then PWA is basically your way to achieve that.
Java app, running on browser without PWA
Same app, running standalone with PWA
I’ve been wondering about the truth of the previous statement: can we create a PWA in, let’s say, pure Java? I did some research and in this article, I’m going to present my findings.
For Vaadin developers:
- This is a perfect summary for you, saving a lot of time and hassle. Follow the quick steps, and you get a PWA immediately.
For Java web developers:
- This is still relevant for you. Many concepts are the same for any Java web app.
In the following Devoxx presentation, Vaadin Expert Teemu Suo-Anttila and myself showed how it’s possible to create a PWA with a Java UI framework such as Vaadin:
Extracting the content of that video here, but I’ll not use Spring this time, I’ll keep it as much generic as possible. Here are the steps summary:
Dismantling a PWA. We will take a quick look at an existing PWA and compare it with a normal Java app, to understand the major differences and what needs to be implemented.
Building a PWA.
Add a service worker, It will help us implement modern browser functionalities, and other features that do not require a running web app, such as background sync, caching, and push notifications.
Specify files to be cached, this makes the app load faster in future visits, and makes a part of it available offline.
Update the service worker, what are the best practices to update the service worker when we add more functionalities to it?
Making it app-like. And this part is magically done with the help of the browser, if we only include a standard manifest file.
To practically test that a web application follows PWA specs, there is a tool called lighthouse that generates a report showing how compatible you app is with the PWA specs.
Let’s start by looking at the state of a Java-based application today, by creating a very simple Vaadin application and then running the lighthouse report for it. Unfortunately, at this point I get 22% compatibility and the report shows a lot of missing features in this simple app.
Related commit: Step 0 | Simple Vaadin App
Reverse engineering an already existing PWA is my favorite way to understand how it works. Luckily there are many templates in the open source web that give some guidance on how to start. For example, if you follow the guide in this GitHub project generator-pwa, you will end up with a minimalistic web project that follows most of PWA specs. Lighthouse gives it a score of 88% and we can easily see that there are a couple of small problems, and since we are testing locally, it’s not secure over HTTPS and thus not a perfect score. But still good enough to start with, and let’s make it our benchmark for a PWA with Java.
Related commit: Step 1 | Simple JS PWA
Now we have a working reference to copy from. Yes, I mentioned it right, I will copy the code. The idea here is, I’ll decrease the learning curve. Instead of reading many references telling what to implement, a service worker, full screen, manifest etc, I will just copy working code and build based on it.
At the bottom of the PWA template’s
And by opening
app.js we find that it’s responsible for registering and communicating with the service worker. Here is the code responsible for registering the service worker
Let’s start by copying
app.js into our Vaadin app. The best location to place it would be under
webapp/VAADIN, so I create a new folder called
js and place it there.
To link to
app.js inside our Vaadin app, we can use the
Related commit: Step 2 | Add JS responsible for SW
To give a correct scope for the service worker, it must be placed in the context-root of our Java application. That’s why I will copy
sw.js to the
resources folder, and update the reference in
We don’t want the
‘/’ scope but the current context-path given by the application server is
‘java-pwa’ in my case here, or
‘./’ in general.
To be able to inject the service worker in the root of the application, we need to make some modifications in the default servlet to handle this specific request. Here is a code that handles
./sw.js request and serves the content of our
Related commit: Step 3 | Add service worker
sw.js we find a reference to the files to be cached:
I removed irrelevant files from the
filesToCache list and update paths:
I kept the
/images/touch/chrome-touch-icon-192x192.png to be cached, because it’s needed by the app shortcut icon. You can either copy the one provided in the template, or add your own.
The lighthouse score at this point is 34%, it seems there is some progress. At least we can see a detected service worker, and those cached files make the application actually work offline!
Related commit: Step 4 | Update cached files
In our PWA template, the functionalities of the service worker were kept to minimum, to make it just work as a proof of concept. But how to update the service worker running on the client, once we add extra functionalities to it?
Just like any installed app, there is a life cycle in which, at some point you must kill the running app, and replace it with a new one.
The most interesting fact with the service worker is that, once its code is modified on the server, the browser will download the newer version and attempt to ‘install’ it. By default the new version will be on a ‘waiting’ state, till the currently opened page is closed. Google explains the process nicely, but there is more you can do!
So you open the web page, it downloads a new service worker, installs it, but still uses the older version? You can overcome this by ordering it to skip waiting. This would be done in our code as the following:
We will then have to claim the current active client as following:
Related commit: Step 9 | Skip waiting when updating the service worker
The previous report shows that some tags are missing from the header of the app, such as
meta viewport, and more importantly the
The manifest file is needed to install the app on the homescreen and it contains some information about the app, such as the name, description, icon for splash screen etc. We need to link to it in the
<head> tag, so I will use the HeaderTags Add-on which will help me modify the bootstrap easily.
After installing the maven dependency, I can do something like this:
And include the
webapp/VAADIN directory, after making some modifications to be relevant to our app. At least the
start_url should be updated, as well as the icons list.
Related commit: Step 5 | Include manifest file
We can also include some other header tags, as pointed out in the report. At this point we have a score of 86%, which is a huge improvement from the original 22%.
Related commit: Step 6 | Add relevant meta tags
The score is not 88% yet, we can see a slight improvement once we turn on production mode, compress resources, and cache widgetset.
Related commit: Step 7 | Compression and production mode
There are some other small limitations like wrong tabindex, and no possibility to inject
app.js at the bottom of the page instead of head. But the good news is, Vaadin Framework Team promised to make this way easier and much more straightforward in upcoming Vaadin releases (Vaadin 8.x maybe?). PWA is still a new concept, but our team is making sure to incorporate all new functionalities and make them part of the framework and ready to use once standardized.
sw.js file. I’m working on improving this to make it even easier, I’ve heard many suggestions from the R&D team and I’m exploring more ways to make the experience pure Java and with the least amount of steps possible.
Spoiler alert! Right now I’m working on getting web notifications to work, as well as a fallback screen when offline.
Do you have more ideas? Contributions?
What else would you like to see as part of the PWA specs in your app?
The GitHub project is waiting for your input, you can also place your ideas as comments below.
Here is the final app with some data, and working on mobile:
Related commit: Step 8 | Show data in a working app
Get the source code along with detailed steps!
You can also take a look at a similar concept done with Spring boot: https://github.com/tsuoanttila/sportstracker