First, firebase is not the only solution for this, but I like their approach, it is simple and multi platform, and really easy to integrate in a rails application.
Of course Firebase has a lot more features, but to keep this post short, we’ll focus only on this feature today.
Remember that to use it in production your application needs to be acessed through SSL, the ServiceWorker API only works through SSL.
And before we start coding, you’ll need to go to the Firebase Console and create a new application for you there.
But lets start with the Rails application, create a new rails app with the ” rails new app_name” command.
Now create a file named manifest.json in the public directory, this file is simple and will be your Portable Web Application manifest.
{ "name": "My First PWA On Rails", "short_name": "PWAOnRails", "start_url": "/", "icons": [ { "src": "/my_icon.png", "sizes": "256", "type": "image/png" } ], "theme_color": "#000000", "background_color": "#FFFFFF", "display": "standalone", "orientation": "portrait", "gcm_sender_id": "103953800507" }
The sender ID 103953800507 is a fixed number Firebase uses, do not put your project firebase id there.
Then we need to create also in the public directory a file named “firebase-messaging-sw.js” to host the Firebase javascript initialization code, the content of this file is provided by the firebase web framework.
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js'); importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js'); importScripts('/firebase/init.js'); firebase.messaging();
The only missing piece is the firebase/init.js file that will hold your firebase application configuration, the values for this file will come from the application you’ve created previously in the console.
// Initialize Firebase var config = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_APP.firebaseapp.com", databaseURL: "https://YOUR_APP.firebaseio.com", projectId: "YOUR_PROJECT_ID", storageBucket: "", messagingSenderId: "THIS_IS_YOUR_REAL_SENDER_ID" }; firebase.initializeApp(config);
We are almost done with the application initialization, now we need to tell the browsers that we want the service worker loaded, to do that, lets use the firebase javascript code, we’ll just add a similar piece of code to the main application layout.
<script src="https://www.gstatic.com/firebasejs/4.8.1/firebase.js"></script> <script> // Initialize Firebase var config = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_APP.firebaseapp.com", databaseURL: "https://YOUR_APP.firebaseio.com", projectId: "YOUR_PROJECT_ID", storageBucket: "", messagingSenderId: "THIS_IS_YOUR_REAL_SENDER_ID" }; firebase.initializeApp(config); </script>
With that done, we need to start integrating the Firebase API with our rails application (yes, I know, we didn’t do anything in rails yet…), and to start we’ll create another javascript file, now in our application assets file, I’ll call it “first_pwa.js”.
function FirstApp() { this.saveMessagingDeviceToken = function () { firebase.messaging().getToken().then(function (currentToken) { if (currentToken) { console.log('Got FCM device token:', currentToken); $.post("/push_registrations", {subscription: currentToken}); } else { // Need to request permissions to show notifications. this.requestNotificationsPermissions(); } }.bind(this)).catch(function (error) { console.error('Unable to get messaging token.', error); }); } this.requestNotificationsPermissions = function() { console.log('Requesting notifications permission...'); firebase.messaging().requestPermission().then(function() { // Notification permission granted. this.saveMessagingDeviceToken(); }.bind(this)).catch(function(error) { console.error('Unable to get permission to notify.', error); }); }; } var firstApp = new FirstApp(); firstApp.saveMessagingDeviceToken();
This code will ask the user for permissions to show notifications, these notifications work online or offline, and more importantly will send the firebase messaging token to the “push_registrations” controller, now we just need to create this controller, use the approach you prefer, I just create the file using a text editor, the content for now is really simple, just to show how to use it…
class PushRegistrationsController < ApplicationController def create puts params[:subscription] User.find_or_create_by push_sub: params[:subscription] end end
We are saving the user subscription ID in a User model, for this sample, I just created the model with the command:
rails g model user push_sub:string
And we can create another controller to broadcast messages to everyone that has already opened the application, but to do that we’ll need a REST client, the easier to use for this sample is the ‘rest-client’ gem, please add the following entry to the Gemfile and run “bundle install”
gem 'rest-client'
You’ll need to get a server application key for your Firebase Messaging app from their web site.
And the broadcast controller will look similar to this:
class BroadcastsController < ApplicationController def index headers = {"Content-Type": "application/json", "Authorization": "key=YOUR_SERVER_KEY"} url = "https://fcm.googleapis.com/fcm/send" User.find_each do |user| payload = { "notification": { "title": "We have a message for you!", "body": "Answer please, we are cool!", "icon": "/app_icon.png", "click_action": "https://oursecureurl.domain.com/chats" }, "to": user.push_sub } RestClient.post(url, payload, headers) end end end
If the application is not running in the moment you send the message, a notification will be displayed for the user automatically, but if the application is running, meaning, if the user is in your web site, you need to handle the message in your code, the code is still simple, just a little more javascript.
Lets open the “firebase-messaging-sw.js” and change the last line and add a few more:
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-app.js'); importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js'); importScripts('/firebase/init.js'); const messaging = firebase.messaging(); messaging.onMessage(function(payload) { console.log("Message received. ", payload); // ... });
Of course you can use the firebase API to create topics and device groups to make it easy to send message to many devices of one user, or to notify everyone that a specific product is on sale.
But this is the basics for the first PWA on Rails with offline notifications.