Progressive Web Apps describe a collection of technologies, design concepts, and Web APIs. They work in tandem to provide an app-like experience on the mobile web. In this tutorial, we'll discuss diverse recipes and show what it takes to turn any application into a powerful, fully-optimized progressive web application using Firebase magic.
This article is an excerpt taken from the book,' Firebase Cookbook', written by Houssem Yahiaoui. In this book, you will learn how to create cross-platform mobile apps, integrate Firebase in native platforms, and learn how to monetize your mobile applications using Admob for Android and iOS.
We'll see how we can fully integrate the FCM (Firebase Cloud Messaging) with any Nodejs application. This process is relatively easy and straightforward, and we will see how it can be done in a matter of minutes.
Let's ensure that our working environment is ready for our project, so let's install a couple of dependencies in order to ensure that everything will run smoothly:
~> npm install fcm-push --save
By using this command, we are downloading and installing the fcm-push library locally. If this is executed, it will help with managing the process of sending web push notification to our users.
const FCM = require('fcm-push');
Congratulations! We're typically done. In subsequent recipes, we'll see how to manage our way through the sending process and how we can successfully send our user's web push notification.
Service workers were, in fact, the missing piece in the web scenes of the past. They are what give us the feel of reactivity to any state that a web application, after integrating, can have from, for example, a notification message, an offline-state (no internet connection) and more. In this recipe, we'll see how we can integrate service worker into our application.
Service workers files are event-driven, so everything that will happen inside them will be event based. Since it's JavaScript, we can always hook up listeners to any event. To do that, we will want to give a special logic knowing that if you will use this logic, the event will behave in its default way.
You can read more about service workers and know how you can incorporate them--to achieve numerous awesome features to your application that our book won't cover--from https://developers.google.com/web/fundamentals/getting-started/primers/service-workers.
Services workers will live in the browser, so including them within your frontend bundle is the most suitable place for them.
//[*] Importing Firebase Needed Dependencies
importScripts('https://www.gstatic.com/firebasejs/
3.5.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/
3.5.2/firebase-messaging.js');
// [*] Firebase Configurations
var config = {
apiKey: "",
authDomain: "",
databaseURL: "",
storageBucket: "",
messagingSenderId: ""
};
//[*] Initializing our Firebase Application.
firebase.initializeApp(config);
// [*] Initializing the Firebase Messaging Object.
const messaging = firebase.messaging();
// [*] SW Install State Event.
self.addEventListener('install', function(event) {
console.log("Install Step, let's cache some
files =D");
});
// [*] SW Activate State Event.
self.addEventListener('activate', function(event) {
console.log('Activated!', event);});
Within any service worker file, the install event is always fired first. Within this event, we can handle and add custom logic to any event we want. This can range from saving a local copy of our application in the browser cache to practically anything we want.
{ "name": "Firebase Cookbook", "gcm_sender_id": "103953800507" }
For this to work, we're doing the following:
Also, within our manifest.json file, we're adding the following metadata:
By now, we've integrated our FCM server and made our service worker ready to host our awesome custom logic. Like we mentioned, we're about to send web push notifications to our users to expand and enlarge their experience without application. Lately, web push notification is being considered as an engagement feature that any cool application nowadays, ranging from Facebook and Twitter to numerous e-commerce sites, is making good use of. So in the first approach, let's see how we can make it happen with Socket.IO.
In order to make the FCM server aware of any client--basically, a browser--Browser OEM has what we call a registration_id. This token is a unique token for every browser that will represent our clients by their browsers and needs to be sent to the FCM server.
~> npm install express socket.io --save
const express = require('express');
const app = express();
app.io = require('socket.io')();
// [*] Configuring our static files.
app.use(express.static('public/'));
// [*] Configuring Routes.
app.get('/', (req, res) => {
res.sendFile(__dirname + '/public/index.html');
});
// [*] Configuring our Socket Connection.
app.io.on('connection', socket => {
console.log('Huston ! we have a new connection
...');
})
So let's discuss the preceding code, where we are simply doing the following:
<script src="/socket.io/socket.io.js"></script>
<script> var socket = io.connect('localhost:3000'); </script>
In the preceding script, we're connecting our frontend to our socket.io backend. Supposedly, our server is in port 3000; this way, we ensured that our two applications are running in sync.
//[*] Importing Firebase Needed Dependencies
importScripts('https://www.gstatic.com/firebasejs/
3.5.2/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/
3.5.2/firebase-messaging.js');
// [*] Firebase Configurations
var config = {
apiKey: "",
authDomain: "",
databaseURL: "",
storageBucket: "",
messagingSenderId: ""
};
//[*] Initializing our Firebase Application.
firebase.initializeApp(config);
// [*] Initializing the Firebase Messaging Object.
const messaging = firebase.messaging();
Everything is great; don't forget to import the app.js file within your index.html file. We will now see how we can grab our registration_id token next:
messaging.requestPermission() .then(() => { console.log("We have permission !"); return messaging.getToken(); }) .then((token) => { console.log(token); socket.emit("new_user", token); }) .catch(function(err) { console.log("Huston we have a problem !",err); });
We've sent out token using the awesome feature of socket.io. In order to get it now, let's simply listen to the same event and hope that we will get some data over our NodeJS backend. We will now proceed to learn about receiving registration token:
socket.on('new_user', (endpoint) => { console.log(endpoint); //TODO : Add endpoint aka.registration_token, to secure place. });
// [*] Configuring our Socket Connection. app.io.on('connection', socket => { console.log('Huston ! we have a new connection ...'); socket.on('new_user', (endpoint) => { console.log(endpoint); //TODO : Add endpoint aka.registration_token, to secure place. }); });
Now, we have our bidirectional connection set. Let's grab that regitration_token and save it somewhere safe for further use.
In the first part of the recipe, we did the following:
Let's discuss what we did previously in the section where we talked about getting the registration_token value:
Remember the socket.io event's name because we'll incorporate that into our further recipes.
In a different approach from the one used in Socket.io, let's explore the other way around using post requests. This means that we'll use a REST API that will handle all that for us. At the same time, it will handle saving the registration_token value in a secure place as well. So let's see how we can configure that.
~> npm install express body-parser --save
Let's discuss what we just did:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
// [*] Configuring Body Parser.
app.use(bodyParser.json());
// [*] Configuring Routes.
app.post('/regtoken', (req, res) => {
let reg_token =req.body.regtoken;
console.log(reg_token);
//TODO : Create magic while saving this token in
secure place.
});
In the preceding code, we're doing the following:
messaging.requestPermission() .then(() => { console.log("We have permission !"); return messaging.getToken(); }) .then((token) => { console.log(token); //[*] Sending the token fetch("http://localhost:3000/regtoken", { method: "POST" }).then((resp) => { //[*] Handle Server Response. }) .catch(function(err) { //[*] Handle Server Error. }) }) .catch(function(err) { console.log("Huston we have a problem !", err); });
Let's discuss what we just wrote in the preceding code:
Since we saw the different approaches to exchange data between the client and the server, in the next recipe, we will see how we can receive web push notification messages from the server.
We can definitely say that things are on a good path. We managed to configure our message to the server in the past recipes. Now, let's work on getting the message back from the server in a web push message. This is a proven way to gain more leads and regain old users. It is a sure way to re-engage your users, and success stories do not lie. Facebook, Twitter, and e-commerce websites are the living truth on how a web push message can make a difference in your ecosystem and your application in general.
Let's see how we can unleash the power of push messages. The API is simple and the way to do has never been easier, so let's how we can do that!
// [*] Special object let us handle our Background Push
Notifications
messaging.setBackgroundMessageHandler(function(payload)
{ return
self.registration.showNotification(payload.data.title,
body: payload.data.body);
});
Let's explain the preceding code:
var fcm = new FCM('<FCM_CODE>'); var message = { to: data.endpoint, // required fill with device token or topics notification: { title: data.payload.title, body: data.payload.body } }; fcm.send(message) .then(function(response) { console.log("Successfully sent with response: ", response); }) .catch(function(err) { console.log("Something has gone wrong!"); console.error(err); }) });
Now, let's discuss the code that what we just wrote:
Congratulations! We're done; now go and test your awesome new functionality!
In the previous recipes, we saw how we can send a normal notification. Let's add some controllers and also learn how to add some pictures to it so that we can prettify it a bit.
// [*] Special object let us handle our Background
Push Notifications
messaging.setBackgroundMessageHandler(function(payload)
{
const notificationOptions = {
body: payload.data.msg,
icon: "images/icon.jpg",
actions: [
{
action : 'like',
title: 'Like',
image: '<link-to-like-img>'
},
{
action : 'dislike',
title: 'Dislike',
image: '<link-to-like-img>'
}
]
}
self.addEventListener('notificationclick',
function(event) {
var messageId = event.notification.data;
event.notification.close();
if (event.action === 'like') {
console.log("Going to like something !");
} else if (event.action === 'dislike') {
console.log("Going to dislike something !");
} else {
console.log("wh00t !");
}
}, false);
return
self.registration.showNotification(
payload.data.title,notificationOptions);
});
Let's discuss what we've done so far:
We saw how to build powerful progressive applications using Firebase. If you've enjoyed reading this tutorial, do check out, 'Firebase Cookbook', for creating serverless applications with Firebase Cloud Functions or turning your traditional applications into progressive apps with Service workers.
What is a progressive web app?
Windows launches progressive web apps… that don’t yet work on mobile