July 2023 Update: This solution relies on Google legacy API to send notifications and this API is being decommissioned by Google in Feb 2024. Read this blog post to learn how to update this app to work past Feb 2024.

Push Notifications are opt-in alerts which notify users of application specific events. They are tied to a system in which the application is installed and they are delivered regardless of whether the application is actively running or not. They often enable a user to take a specific action.

Modern web applications can take advantage of Notification API to create and manage content of notifications. It is the application user who is in control of whether an application can show notifications or not. The API has methods to ask for the user's consent and without this consent the application will not be able to show any notifications. The consent is stored in the browser running the web application and any changes to the consent (revoking it or granting it) need to be done in the browser itself.

The Notification API is supported by all major platforms. For Apple devices the support has been added since iOS 16 (which as of May 2023 is in Beta version)

The "push" part of Push Notifications represents the ability of notifications to be delivered ("pushed") to all application users. This part is not covered by Notification API specification and requires a dedicated service. A web application can implement it from scratch or, more commonly, rely on a third party provider.

Local Events Demo Application

How to implement the Push Notifications in a Visual Builder web application will be demonstrated on a simple example app called "Local Events". It is an app where authenticated users can create and "announce" an event happening in their local community. Events can be categorized (Art, Sport, Communal) and users can configure which type of events they want to be notified about.

Local Events main page

The source code of the application is appended at the end of this blog post with notes on how to make it work. The rest of this blog post will explain how the application was implemented in detail. For best understanding download the app and import it into your VB instance and follow the code flow as it is being explained.

Push Service

As said earlier, the delivery of notifications requires an external service. Which service to use and how to use it is beyond the scope of this blog post. The example app is using free Firebase Cloud Messaging by Google and in principle other services will provide similar capabilities, that is:

  • contact the service to receive a unique token representing current user+device pair
  • tell service to send a notification data to user+device pair using that token

Application Anatomy

As seen on the above screenshot the application is a simple CRUD app with four Business Object entities to store application data. These entities are:

  1. Event – list of events. Each event has name, description, datetime and type
  2. EventType – LOV with three above mentioned event types
  3. Preferences – each user (identified by email) can configure which types of events they want to be notified about
  4. Sessions – list of active messaging service tokens per user (ie email) and their device (random hash identifying their device)

This post assumes you are familiar with how to create a Business Object model and how to create Visual Builder pages to change the model.

Implementation

The implementation of Push Notifications revolves around specific instructions of the push notifications service you are using and how the service is expected to be used. But regardless of which service is used, which in this case is Firebase Cloud Messaging(FCM), the steps will be in principle similar.

Load the Push Notification library

Each push messaging service will come with an API library to load. For the FMC this is done in index.html where libraries are imported and references to required methods are stored in a globally scoped variable:

<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
import { getMessaging, getToken, onMessage } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-messaging.js";

// make firebase API accessible to VB pages:
window.VB_FB = {
  initializeApp : initializeApp,
  getMessaging : getMessaging,
  getToken : getToken,
  onMessage : onMessage,
};
</script>

Initialize the Push Notification library

Once the library is loaded it needs to be initialized. This is done in the vbEnter event handler at the application level (ie startNotificationsListerner chain) and it usually involves running a custom JS code (ie AppModule.startFirebase function) which calls some library APIs as documented in your Push Notification library documentation (for example FCM). The goals here are:

  1. connect to the service and in return receive a token which will identify this user+device pair. Such a token is then stored in the Sessions entity so that whenever a new event is created in the app, there is a list of tokens representing active users which need to be notified. The token is received in a custom JS function and in order to make it accessible to the VB app a custom event Token has been defined and fired from the function with received token value. See registerClient chain in the app for how the token is persisted
  2. use the service API to start listening on push notifications. This often requires to distinguish two application states which needs to be handled differently:
    1. the application is active, that is user is looking at the app (ie. "FCM.onMessage" handler). Handling of a push notification is the same as handling a regular DOM event. The app in this case does two things – have a look at the handler of the "notification" custom event at the application level and also in the main-events page. The former uses FireNotification to notify users and tell them that new event was created. The later, executed only if user is looking at the main-events page, simply refreshes the table listing events to automatically show the newly added event.
    2. the application is not active, that is user is looking at different tab in their browser or minimized browser/PWA app completely (ie. "FCM.onBackgroundMessage" handler). To handle this case a service worker has to be used so that even when the Local Events app is inactive (not running) the service worker will keep listening in the background on push notifications and handle them. Have a look at source code of the webApps/events/firebase-messaging-sw.js file which implements the service worker. When the worker receives an event, it uses the Notification API to show a notification with data from event's payload (eg. eventID and eventName). It also hooks a click listener on the notification which simply opens LocalEvents app on event's detail page (main-event). That is implemented by using a hardcoded app URL with eventId as a parameter. As for the registration of this service worker, that is done in the startFirebase function we discussed earlier. 
  3. use the Notification.requestPermission() API to request permissions from the app user to show notifications

The application is now ready receive push notifications.

Pushing notifications to clients

The application users are notified when a new event record is created. How that is implemented can be seen in the main-events page in the CreateNewEventRecordChain chain. After a new record is persisted what happens is:

  1. all active sessions are fetched (and tokens to notify those users)
  2. all user preferences are fetched
  3. new event record with all above data is passed into the notifyClientAboutNewEvent method which:
    1. iterates over active user sessions and based on the type of newly created event, it filters out users who are interested in this new event
    2. uses messaging service specific API (eg. a REST endpoint call) to send a push notification using the token identifying user+device pair

Conclusion

And that is it. The application is finished now and can be run. A few things to be aware of:

  • Sometimes notification delivery is not instant and can take up to several minutes. That is fully at discretion of the messaging service being used and is not fault of your VB application
  • A VB application can be run in three different stages – design preview, staged, live. Each of these stages are using their own database, but the demo app does not make any distinction of those and uses the same FCM messaging app for all three stages. Which can lead to erroneous states that a record created in the development preview stage is notified to the staged version of the app which does not have that record. Proper solution would use different FCM messaging apps for different stages.

Appendix – How to Make Demo Application Work

July 2023 Update: This solution relies on Google legacy API to send notifications and this API is being decommissioned by Google in Feb 2024. Read this blog post to learn how to update this app to work past Feb 2024.

 

The sources provided are stripped of FCM specific keys – if you want to run the app you will have to create your own free FCM account and use API keys you receive. Specific places which needs to be updated are (search the file for quoted name and follow instruction provided in the code comments):

  1. webApps/events/firebase-messaging-sw.js – initialize "firebaseConfig" configuration variable
  2. webApps/events/firebase-messaging-sw.js – update your app URL in "notificationclick" listener
  3. webApps/events/app-flow.js – initialize "firebaseConfig" configuration variable
  4. webApps/events/app-flow.js – provide your "vapidKey"
  5. webApps/events/flows/main/pages/main-events-page.js – provide FCM "authorization" key value