A fairy tale about dynamic manifest.json files: The journey continues…

Hello folks and welcome to the next chapter of our fairy tale about dynamic manifest.json files. In the first chapter we’ve seen the context behind some of the decisions as well as some of the challenges that I’ve faced. But now… now we go to the fun part. In a quick roundup, by now we have:

  • Required resource files (manifest.json, pushwoosh-service-worker.js and the SDK loaded asynchronously)
  • All the required OutSystems developments for the previous bullet.

As someone would say… All aboard the night train!

The interface components

Before moving forward and start dwelving into the code per se, let me explain first how I’ve structured the elements. When you reference the PWWebNotifications eSpace, you’ll have one public UI element: a Web Block named PushwooshPermission – and this is our “central” element. This element, despite simple, has a crucial task: opening the required URL so we can register notifications from pretty much anywhere in our system. Pushwoosh, by default, supports this – on their Web Push SDK 3.0 there is the following reference:


Add the mechanism that redirects users to this subdirectory on your site like yourdomain.com/push-notifications/index.html. Make sure to set up our SDK in the subdirectory as well!


But as Jesse Ventura would say, “I ain’t got time to redirect”. So, in order to “bypass” this step, I use an iframe (WebPatterns.Utilities.IFrame) to load the page – not only this, but we’ll see the remaining later. The URL on this widget is the following:


“/PWWebNotifications/PushwooshRequest.aspx?GCM=” + GCM + “&Name=” + Name + “&ShortName=” +
ShortName + “&SafariId=” + SafariId + “&ApplicationCode=” + ApplicationCode


The PushwooshRequest is the screen in which two Web Blocks are invoked: PWInitialization and PWRequestPermission. Splitting it up:



This block is responsible, as the name says, for the initialization of the SDK. In order to achieve that, we use the following snippet of code:


<script type='text/javascript'>
    var Pushwoosh = Pushwoosh || [];
        Pushwoosh.push(['init', {
          logLevel: 'error', // possible values: error, info, debug
          applicationCode: '" + EncodeJavaScript(ApplicationCode) + "',
          safariWebsitePushID: '" + EncodeJavaScript(SafariId) + "',
          defaultNotificationTitle: '" + EncodeJavaScript(Name) + "',
          defaultNotificationImage: '" + EncodeJavaScript(LogoPath) + "',
          autoSubscribe: true,
          userId: " + GetUserId() + ",
          scope : '/" + EncodeJavaScript(eSpaceName) + "/'


If you see the above code, there are three lines highlighted:

  • autoSubscribe prompts your users immediatly if they want to receive notifications. This is useful if you have already applied some logic – or if you want to request permission on certain steps of a flow. If you set this to false, you’ll have to request permission invoking the following public method: Pushwoosh.subscribe()
  • userId sets, well… the user identifier for a given device registration. I’m not one-hundred-percent sure but, since this is not mandatory, this is the same as invoking the api.registerUser(userId) method. (Note to self: confirm this)
  • scope sets the scope of the pushwoosh-service-worker.js location. In our case, since we’re implementing this in one specific module for reusability, we’ve set the scope as our module eSpace name. This is a local variable, set on the preparation using the famous GetOwnerEspaceIdentifier() function (built-in).

The other lines are also important, with an “honorable mention” for the defaultNotificationTitle and the defaultNotificationImage – the name says it again: it’s default, we can change it when sending the actual notification.



Contrary to the PWInitialization, the PWRequestPermission block is fairly simple since it only has four different functions: let us know that the plugin is ready (onReady), register user and set a session variable if permission is granted (onPermissionGranted), unregister current device and set a session variable if permission is denied (onPermissionDenied) and, finally, let us know what is the outcome of the decision – if we want to do any server-side logic with the decision – through the  HasGrantedPermission server action.


<script type='text/javascript'>
    $(document).ready(function() {
        Pushwoosh.push(['onReady', function(api) {
            console.log('Pushwoosh ready');
        Pushwoosh.push(['onPermissionGranted', function(api) {
            " + If(GetUserId() <> NullIdentifier(), "api.registerUser(" + GetUserId() + ");", "") + "
            $('#" + OnPermissionGranted.Id + "').click();
        Pushwoosh.push(['onPermissionDenied', function(api) {
            $('#" + OnPermissionDenied.Id + "').click();

And now… how can we create a dynamic manifest.json?

Ok. So far we have a working version. But what if I want to use this on a different projects? Or, even more important, what if I want to share this with the OutSystems community? I need to make this reusable! Well, it was a very, very long process of about… 20 minutes, approximately. My first approach: go to Skype, reach Vera’s contact and ask her if she knew a way to create a dynamic resource file. We quickly come to the conclusion that this was a non-optimal approach. So I wondered: “The manifest.json file is a – ta-daaaaaa – JSON file. What if I had a REST webservice that would return the contents, passing the dynamic fields as input parameters?”

And that was exactly what I’ve done:


  1. Define the service…
  2. …set the output…
  3. …and invoke it!



On the rendered web page, this is the end result:




That’s all for now, folks. In the next few days, I’ll upload a new version of the forge as well as the sample eSpace so you can do your own tests – this article will be updated as well. As usual, any question, comment or suggestion, feel free to let me know here, through contact form or through Twitter.





PS: If anyone has a macOS with an Apple Developer account and is willing to spare a couple dozen of minutes to make sure the Safari part is working properly, I’ll be very grateful 🙂


Leave a Reply