pwa-quasar-local
This project demonstrates how to develop a Progressive Web Application (PWA) locally on an Android device, using the Quasar Framework v2.
The main goal is to run an application in hot-reload mode, and make it available to a physical Android device. I'm using Android (Samsung Galaxy S10, Android 11), but I'm sure there's a way to tweak this project to make it work on iOS.
Final result
Table Of Contents
- Challenges
- Requirements
- PWA with hot-reload via HTTP
- Setup A Local DNS Server
- Access PWA From Local Machine
- Access PWA From An Android Device
- Set An HTTPS Connection From Local Machine To PWA
- Set An HTTPS Connection From An Android Device To PWA
- Controlling The Android Device With Google Chrome
- Conclusions
- Local Development
- Troubleshooting
- Useful Resources
- Authors
- License
Challenges
This is how it all happened - documenting my learning process for future me
- I wanted to know if a PWA can send Push Notifications, like a normal application. For example, if the device is locked up and quiet, will it ring and buzz? Will I get notification from the PWA?
- To make it work, I wanted to have a local development environment in hot-reload mode, so I can test the PWA on an Android device as the code changes. Previously mentioned, I'm on Samsung Galaxy S10, Android 11, so if you're using a different Android device or version, make sure to Google for the "deltas".
- The requirements for running a PWA are very restricting when it comes to HTTPS and local development. Quasar makes it possible to use HTTPS for local development out-of-the-box. Unforuneately, the HTTPS trick works for https://localhost, so how can an Android device access this "local network address" and load the PWA? I have a way to mask
localhostwith a desired domain so it'll behttps://test.meirg.co.il, but that still doesn't solve the problem of making it accesible to other devices on the local network. - Let's get to business
Requirements
Local Installation
- NodeJS v14.17.0+
- yarn
npm install --global yarn && \ yarn --version - quasar cli - see yarn global if you're using nodemon
Install NPM packages
yarn global add @quasar/cli
cd awesome-pwa && yarn install
- Google Chrome for viewing the PWA and controlling remote devices, such as the target Android device.
- (Optional) I'm using Visual Studio Code, which makes the whole development process very easy. Especially the linting and auto-formatting.
Docker
Instead of installing the above requirements and dnsmasq, you can use Docker to simplify the process.
- Docker build image
cd awesome-pwa docker build -t unfor19/awesome-pwa:dev --target dev .
- Docker run container - Replace FQDN and IP address according to local network IP address.
# Executes quasar dev --mode pwa # Waits for build process to complete and prints App URL # In the background - runs dnsmasq local DNS server docker run --rm -it \ -p 8080:8080 \ -p 443:443 \ -p 53:53/udp \ -v "$PWD":/usr/src/app unfor19/awesome-pwa:dev "meirg.co.il.test" "192.168.0.5"
PWA with hot-reload via HTTP
Before you read along, this project is the final artifact of the below steps. You can clone/fork this project, or even generate a GitHub repository from this project, and simply move on to the Usage section.
- Generate a PWA project with Quasar
quasar create awesome-pwa
- Project name:
awesome-pwa - Project product name:
Awesome PWA - Project description:
Testing PWA on a physical Android device - Author:
firstName lastName <email@address.com> - Pick your CSS preprocessor:
Sass with SCSS syntax - Check the features needed for your project:
ESLintTypeScript- make sure to select TypeScript with<space>and only then hit<enter>
- Pick a component style:
Composition API - Pick an ESLint preset:
Prettier - Continue to install project dependencies after the project has been created?
Yes, use Yarn
- Project name:
- From now on the working directory should be awesome-pwa
- Add PWA to the project with
quasar mode add pwa
... App • Creating PWA source folder... App • Copying PWA icons to /public/icons/ (if they are not already there)... App • PWA support was added - Run the application locally in hot-reload mode
quasar dev --mode pwa
- Browser should be automatically opened, serving http://localhost:8080/#/. This is the application running locally on your machine, and any code change will immediately be applied to the app.
- To make sure Service Workers are loaded properly, set the browser settings for the application:
- Navigate to http://localhost:8080/#/
- Open Chrome DevTools
- Application > Service Workers > Tick Bypass for network
Setup A Local DNS Server
IMPORTANT: When using Docker, follow only the instructions marked with 🐳.
-
We need a local DNS server to trick everyone on the local network to think that
https://meirg.co.il.testis actually my local machine network address, which is192.168.0.5at the moment of writing. -
I chose dnsmasq for the job, but I'm sure any other option is valid. On macOS, use brew to install dnsmasq. If you're on Windows, I suggest you use WSL2 (TODO: Add docs for WSL2)
brew install dnsmasq -
🐳 Check your local machine network IP address
ipconfig getifaddr en0 # Mine is 192.168.0.5 -
Edit dnsmasq config, and add map a domain to your local network address
vim /opt/homebrew/etc/dnsmasq.conf
# Maps a local ".test" domain to the local network ip address of the current machine # Make sure to use ".test" as a suffix # Change "meirg.co.il" with your domain and "192.168.0.5" with your local network IP address address=/meirg.co.il.test/192.168.0.5
-
🐳 Map the local domain
meirg.co.il.testto our local machine192.168.0.5- macOS - Create the directory "test" under /etc/resolver, see https://vninja.net/2020/02/06/macos-custom-dns-resolvers/ - any domain under
*.testwill resolve to192.168.0.5which issudo mkdir -p /etc/resolver/test
- WSL2/Linux - Edit
/etc/hostsfilemeirg.co.il.test 192.168.0.5
- macOS - Create the directory "test" under /etc/resolver, see https://vninja.net/2020/02/06/macos-custom-dns-resolvers/ - any domain under
-
Restart
dnsmasqby stopping and starting it- macOS
sudo brew services stop dnsmasq sudo brew services start dnsmasq
- macOS
-
🐳 Flush (refresh) DNS
- macOS
sudo killall -HUP mDNSResponder
- macOS
-
🐳 Check your local DNS server
# This is what happens when you use the default DNS server dig meirg.co.il.test # returns a.root-servers.net. nstld.verisign-grs.com. 2021113002 1800 900 604800 86400 # And now via dnsmasq local DNS server dig meirg.co.il.test @192.168.0.5 # returns 192.168.0.5
Access PWA From Local Machine
Assuming quasar dev -m pwa is running in the background.
Everything is already set, all you gotta' do is open Google Chrome and navigate to http://meirg.co.il.test:8080
Access PWA From An Android Device
Assuming quasar dev -m pwa is running in the background.
All the following steps are done on the Android device.
- Set your Android Device DNS settings, so it will resolve use
192.168.0.5as the DNS server.- Open WIFI settings and change the DHCP settings from Auto to Manual.
- Set DNS records to
192.168.0.5- The local machine which is runningdnsmasqlocal DNS server1.1.1.1- Cloudflare DNS to enable internet access in casednsmasqis not responding
- Open Google Chrome and navigate to http://meirg.co.il.test:8080, the PWA should be accessible and will reload upon changing the application's source code
- That's nice, though it's not why we're here for. Since the application is served via HTTP and not HTTPS, the app is not classified as PWA by the Android device. All the cool features of add-to-home-screen (A2HS) and push-notification won't be available until we set HTTPS.
Set An HTTPS Connection From Local Machine To PWA
The standard process for generating a CA certificate is demonstrated in the below diagram.
Image Source: https://www.ssl.com/faqs/what-is-a-certificate-authority/
- For local development purposes, we're playing both the Applicant and CA roles, this is why I created the "convinience script" called scripts/generate_ca.sh, which does the following:
- Creates the directory
awesome-pwa/.certs, this directory should not be committed to this repo. - Generates the required files
rootCA.key,rootCA.pem,${FQDN}.crt(per domain) and the converted format${FQDN}.der.crtto be installed on the Android device. The script is based on this stackoverflow answer - Prints a
Usagemessage
- Creates the directory
- Execute the "convinience script", scripts/generate_ca.sh, to generate the desired keys and certificates
# Replace domain name ./scripts/generate_ca.sh "meirg.co.il"
- The next step is to tell Quasar's
devServerawesome-pwa/quasar.conf.js to serve HTTPS and use the generated CA certificate and rootCA key.devServer: { https: { cert: '.certs/meirg.co.il.test.crt', key: '.certs/rootCA.key', }, port: 443, open: false },
- The final step is to install the generated
meirg.co.il.test.crtcertificate on your local machine so it can trust the certificate that the PWA is using- macOS
sudo security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" "awesome-pwa/.certs/meirg.co.il.test.crt"
-r trustRootto make the OS trust the certificate and the Certification Authurity (CA). Trusting the CA is required since it's a self-signed certificate, and ourrootCAis considered as a non-trusted CA by the OS. If you install the certificatemeirg.co.il.test.crtby double clicking it, it will be marked as "not trusted" by the OS, as therootCAis not trusted by the OS. - macOS
- Open Chrome browser and navigate to https://meirg.co.il.test, the PWA should be served properly via HTTPS
Set An HTTPS Connection From An Android Device To PWA
Previously, we generated meirg.co.il.test.der.crt, this file is the one that should be installed the Android Device.
-
Local Machine > Upload
awesome-pwa/.certs/meirg.co.il.test.der.crtto Google Drive -
Android Device > Download
meirg.co.il.test.der.crtfrom Google Drive -
Android Device > Settings > Search "CA Certificate" > Install anyway > Select and install
meirg.co.il.test.der.crtfrom local storage -
Android Device > Open Chrome browser and navigate to https://meirg.co.il.test, the PWA should be served properly via HTTPS, see final-result
TIP: To view the installed certificates, in the settings, search for User certificates
Controlling The Android Device With Google Chrome
- First, Configure your Android with Developer Options and Allow USB Debugging. To be on the safe-side, I also downloaded and installed Samsung Smart Switch which includes Samsung Galaxy drivers. At this point I'm not sure if the drivers are necessary, I'll need to uninstall them to find out (TODO: uninstall drivers and see if it affects the installation)
- Connect your Android device to the local machine with a USB cable
- macOS - I used my macOS charger cable to connect since that's only cable I got with TypeC to TypeC
- Open Chrome and navigate to Chrome's
chrome://inspect#devicespage, see Remote debug Android devices - (WIP) The Android device device should appear on the list, so click
inspectto view the contents of the mobile phone, on the local machine's display. It's like using your Android device as an emulator, though stuff is happening for real.
Conclusions
- During the process I realized I can't use
test.meirg.co.il, and I must usemeirg.co.il.test, this is because I'm on macOS, I need to map all*.testtraffic via the local DNS server (dnsmasq), and the trick is to use/etc/resolver/testto do that. On Linux/WSL2, or even Windows, it's way easier, you can simply change the/etc/hostsfile and that's it. - Remote debugging does not work on WIFI, even though I enabled it on my Android device, so I must use a USB cable to make it work. I wonder if I'm doing something wrong.
- I need to read/write a blog post about CA, I feel like this subject is still not 100% clear to me.
- The application is not 100% stable in hot-reload mode and I still need to figure out why.
Local Development
All commands are invoked from the awesome-pwa directory.
- Change dir
cd awesome-pwa - Install depenendcies
yarn install
- Build PWA app
yarn build
- Build SPA app
yarn build:spa
Troubleshooting
DNS_PROBE_FINISHED_NXDOMAIN- The client cannot resolve DNS entrymeirg.co.il.test; The local DNS server,dnsmasq, is not set in the client's device. Fix by runningdnsmasqand setting the DNS server records of the client properly# Run dnsmasq sudo brew services start dnsmasq # Set DNS server in client device, for example, 192.168.0.5
ERR_CONNECTION_REFUSED- Client resolved DNS sodnsmasqworks, but the server is not online, fix by running localquasar devservercd awesome-pwa yarn serveYour connection is not private- Client doesn't have themeirg.co.il.crtinstalled on the local machine, ormeirg.co.il.der.crtinstalled on the Android device. Fix by installing the certificates as instructed in access-pwa-from-local-machine and access-pwa-from-an-android-device.
Useful Resources
- https://developer.chrome.com/docs/devtools/progressive-web-apps/
- https://www.baeldung.com/openssl-self-signed-cert
- https://security.stackexchange.com/questions/20803/how-does-ssl-tls-work
- https://en.wikipedia.org/wiki/X.509
- https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm
Authors
Created and maintained by Meir Gabay
License
This project is licensed under the CC Attribution 4.0 International License - see the LICENSE file for details




