- Install/Update chrome to 60 or above.
- Install chrome web server Chrome Web Server
- Install Chrome in mobile phone
- Install firesbase tool via npm
npm install -g firebase-tools
- Go to workspace folder in your laptop/desktop and clone the repository below
C:\workspace> git clone https://github.com/mohanramphp/pwa-tutorial.git
C:\workspace> cd pwa-tutorial
C:\workspace\pwa-tutorial> cd demo
C:\workspace\pwa-tutorial\demo> cd work
C:\workspace\pwa-tutorial\demo\work>code .
- Go to chrome apps in chrome and launch 200 ok!
chrome://apps/
-
Choose your work folder and stop and start the server and click on the url shown below web browser URL(s)
-
To run the app with initial data
- In index.html, uncomment below line
<!--<script src="scripts/app.js" async></script>-->
- uncomment the following line at the bottom of your app.js file
// app.updateForecastCard(initialWeatherForecast);
- comment the line in #2 again its just to check app works with fake data.
-
Find the below TODO line in app.js and add code below.
// TODO add saveSelectedCities function here
// Save list of cities to localStorage.
app.saveSelectedCities = function() {
var selectedCities = JSON.stringify(app.selectedCities);
localStorage.selectedCities = selectedCities;
};
- Find the below TODO line in app.js and add code below.
// TODO add startup code here
app.selectedCities = localStorage.selectedCities;
if (app.selectedCities) {
app.selectedCities = JSON.parse(app.selectedCities);
app.selectedCities.forEach(function(city) {
app.getForecast(city.key, city.label);
});
} else {
app.updateForecastCard(initialWeatherForecast);
app.selectedCities = [
{key: initialWeatherForecast.key, label: initialWeatherForecast.label}
];
app.saveSelectedCities();
}
- We have to save the selected city in local storage
document.getElementById('butAddCity').addEventListener('click', function() {
// Add the newly selected city
var select = document.getElementById('selectCityToAdd');
var selected = select.options[select.selectedIndex];
var key = selected.value;
var label = selected.textContent;
if (!app.selectedCities) {
app.selectedCities = [];
}
app.getForecast(key, label);
app.selectedCities.push({key: key, label: label});
app.saveSelectedCities();
app.toggleAddDialog(false);
});
Test it out
- When first run, your app should immediately show the user the forecast from initialWeatherForecast.
- Add a new city (by clicking the + icon on the upper right) and verify that two cards are shown.
- Refresh the browser and verify that the app loads both forecasts and shows the latest information.
- Use service workers to pre-cache the App Shell
- Find the below TODO line in app.js and add code below.
// TODO add service worker code here
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('./service-worker.js')
.then(function() { console.log('Service Worker Registered'); });
}
- Add this code to your new service-worker.js file
var cacheName = 'weatherPWA-step-6-1';
var filesToCache = [];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
- Now, reload your page. Open the service worker pane in chrome developer
- Add the following script, below the install event in service-worker.js
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
});
- The activate event is fired when the service worker starts up
- Basically, the old service worker continues to control the page as long as there is a tab open to the page
- Enable update on reload option in Chrome dev tool
- Add the following code to update the cache
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
return self.clients.claim();
});
Case in which the app wasn't returning the latest data so to avoid that
self.clients.claim()
is used
- Replace filesToCache variable declaration
var filesToCache = [
'/',
'/index.html',
'/scripts/app.js',
'/styles/inline.css',
'/images/clear.png',
'/images/cloudy-scattered-showers.png',
'/images/cloudy.png',
'/images/fog.png',
'/images/ic_add_white_24px.svg',
'/images/ic_refresh_white_24px.svg',
'/images/partly-cloudy.png',
'/images/rain.png',
'/images/scattered-showers.png',
'/images/sleet.png',
'/images/snow.png',
'/images/thunderstorm.png',
'/images/wind.png'
];
- Let's now serve the app shell from the cache. Add the following code to the bottom of your service-worker.js file
self.addEventListener('fetch', function(e) {
console.log('[ServiceWorker] Fetch', e.request.url);
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
});
Your app is now offline-capable! Let's try it out. Test it out.
- Reload your page and then go to the Cache Storage pane on the Application panel of DevTools.
- Right click Cache Storage, pick Refresh Caches, expand the section and you should see the name of your app shell cache listed on the left-hand side.
- When you click on your app shell cache you can see all of the resources that it has currently cached.
- In Service Worker pane of DevTools and enable the Offline checkbox. After enabling it, you should see a little yellow warning icon next to the Network panel tab. This indicates that you're offline.
- Reload your page to see the initial rendering with fake data
- Use service workers to cache the forecast data
- Add the following line to the top of your service-worker.js file
var dataCacheName = 'weatherData-v1';
- Update the code in activate event handler
if (key !== cacheName && key !== dataCacheName) {
- Update the fetch event
self.addEventListener('fetch', function(e) {
console.log('[Service Worker] Fetch', e.request.url);
var dataUrl = 'https://query.yahooapis.com/v1/public/yql';
if (e.request.url.indexOf(dataUrl) > -1) {
e.respondWith(
caches.open(dataCacheName).then(function(cache) {
return fetch(e.request).then(function(response){
cache.put(e.request.url, response.clone());
return response;
});
})
);
} else {
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
}
});
- Get data from the cache - Find the TODO add cache logic here comment in app.getForecast() in app.js
//TODO add cache logic here
if ('caches' in window) {
/*
* Check if the service worker has already cached this city's weather
* data. If the service worker has the data, then display the cached
* data while the app fetches the latest data.
*/
caches.match(url).then(function(response) {
if (response) {
response.json().then(function updateFromCache(json) {
var results = json.query.results;
results.key = key;
results.label = label;
results.created = json.query.created;
app.updateForecastCard(results);
});
}
});
}
- Notice how the cache request and the XHR request both end with a call to update the forecast card. How does the app know whether it's displaying the latest data? This is handled in the following code from app.updateForecastCard
var cardLastUpdatedElem = card.querySelector('.card-last-updated');
var cardLastUpdated = cardLastUpdatedElem.textContent;
if (cardLastUpdated) {
cardLastUpdated = new Date(cardLastUpdated);
// Bail if the card has more recent data then the data
if (dataLastUpdated.getTime() < cardLastUpdated.getTime()) {
return;
}
}
The app should be completely offline-functional now. Test it now.
- Save a couple of cities and press the refresh button on the app to get fresh weather data
- then go offline and reload the page.
- go to the Cache Storage pane on the Application panel of DevTools. Expand the section and you should see the name of your app shell and data cache listed on the left-hand side. Opening the data cache should should the data stored for each city.
- Web App Install Banners and Add to Homescreen for Chrome on Android
- Create a file named manifest.json in your work folder and copy/paste the following contents
{
"name": "Weather",
"short_name": "Weather",
"icons": [{
"src": "images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "images/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "images/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
}, {
"src": "images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}, {
"src": "images/icons/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
}],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#3E4EB8",
"theme_color": "#2F3BA2"
}
- Tell the browser about your manifest file. Now add the following line to the bottom of the
<head>
element in your index.html file
<link rel="manifest" href="/manifest.json">
- Add to Homescreen elements for Safari on iOS. In your index.html, add the following to the bottom of the
<head>
element
<!-- Add to home screen for Safari on iOS -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Weather PWA">
<link rel="apple-touch-icon" href="images/icons/icon-152x152.png">
- Tile Icon for Windows. In your index.html, add the following to the bottom of the
<head>
element
<meta name="msapplication-TileImage" content="images/icons/icon-144x144.png">
<meta name="msapplication-TileColor" content="#2F3BA2">
Open up the Manifest pane on the Application panel. you'll be able to see it parsed and displayed in a human-friendly format on this pane.
- Deploying our application in firebase
- Create a new app at https://firebase.google.com/console/
- open cmd prompt from work folder and run
firebase login
- Initialize your app and provide the directory (likely "./")
firebase init
- Finally, deploy the app to Firebase
firebase deploy
- done! Your app will be deployed to the domain: https://YOUR-FIREBASE-APP.firebaseapp.com
Find demo application @ Weather App