I’ve been busy for these weeks, so that’s why I haven’t posted anything yet. To make it quick, I’ve been coding a lot of the ‘boilerplate’ and initial structure that I’ll be using to create different modules for Manapp.
But today I want to share with you some things that I have learn doing this, and also other cool stuff that I have learnt at the office. This is going to be a very light post, no too much technical stuff. I’m trying to tell you some ideas and concepts that have helped me to improve my app and my developer-experience.
First of all: stats. I never paid too much attention to stats. I thought they were useful to know how many people is visiting my website, and the time they spent, but recently I’ve seen that you can track bunch of things and actions, without the need of being Facebook.
Probably, you are using Google Analytics, and you have the power to track events, and even send custom dimensions/measures to analyics.
Right now, I’m developing a Progressive Web App, and one of the dimension that I want to know about my user is the client they are using: The website? or the installed PWA? More interesting dimensions could be:
- Is compatible with Service Workers (If they aren’t, they cannot install the app)
- Is compatible with Push Subscriptions (If they aren’t, my ‘push’ feature is not needed/valuable)
Also, you would like to know some custom numbers, like:
- Times that the ‘install’ banner was shown
- Times that the ‘install’ banner was accepted or dismissed
Even, with events, you could know what links are more ‘attractive’ to the user, or what form elements are more useful, and wich of them are ignored by users.
Remember, you can improve your website only if you know your users’ needs.
If you are a web developer, probably you know what I’m talking about. I already knew about service workers, but I only used them for caching some assets or HTTP requests, but SW are so much powerful than that.
Right now, my SW is able to send/receive message to/from my webapp, so I can get some data from the webapp and store it into IndexDB. On this way, next time you run the webapp, you can load that data (my VueJS store) and initialize your application with some data already rendered (even before first HTTP request has been launched).
My SW is also genereting all the notifications of the webapp. Even if the webapp has to render a notification, it doesn’t. Webapp just sends a message to SW, so SW is taking the responsability of doing that. Thanks to this, all my ‘notification creation’ logic is centralized, and both (browser and push notifications) are going to be created on the same way, with the same tools.
As I recently mentioned, SW is subscribing to push, and this is a very powerful tool. We think on ‘push’ just for displaying notifications when we have the app closed, but the real thing is that we can send any piece of information to our webapp.
- Logout the user (Imagine you need that all your users logout and login again, for updating a new config, just logout everybody and they will be logging in again)
- Update cached config (Imagine you change your logo, you can develop a feature to override some of your cached assets)
- Update persisted data (Imagine you delete something on your webapp, you can develop a feature to modify your IndexDB based on a push message)
- And of course: Show notifications
Modularize your Service Worker
When you have a powerful SW, with some of the features that I mention, you’ll notice that your SW is starting to increase its size.
I suppose you already know about webpack, so you can apply that to your SW, and split it on several files, so each file has its own responsability.
For instance, I have different files for: installing, activating, fetching (and caching), receiving messages from webapp (this file contains the method to send messages to webapp), receiving push events, handling notification clicks and I even created a file with tools for read/write/delete on IndexDB in a simpler way.
Seriously, modularize your service worker. My development performance increased a lot since I did that.
This is something anoying, but we have to be conscient of the reality where we live. Not all browsers support Notifications. Not all browsers support Service Workers. And not all browsers support Push subscriptions. So, you need to check their compatibility if you don’t want your webapp crashes.
I used to do this on app’s launch, or in the same moment I was going to use the feature. I checked if the browser supported SW when I was about to register the SW. Wrong!
Something that I discovered is that you can create a module for each of these tasks. In deed, this is a good idea, because you can pack all the features related to a technology in the same file. Pay attention:
- I created a file named ‘notifications.js’. This file has a function that checks if your browser support notifications, but it has a function to display a notification. In case it is compatible with SW notifications, it sends a message to SW (using a custom function located in ‘service-worker.js’, of course). If SW are not supported, check if Notifications API is supported and create one.
- I also created a ‘legacy-notifications.js’ that has a function to check if Notifications API is supported (and I use it in ‘notifications.js’). Here I also declared a function to create a window.Notification object.
- Other file I created is ‘service-worker.js’ file. This file has a function that checks if SW are compatible with the browser. There are 2 functions related to the SW such as ‘sendMessageToServiceWorker’ and ‘getMessageToServiceWorker’. So in app’s load, I check compatibility with a function located here, and if it’s compatible, I place an eventListener, and its handler is also located here.
- Finally I created a ‘push.js’ that contains tools like check compatibility, subscribe to push or unsubscribe from push. So when a SW is installed and user is logged in, I use tools located here to subscribe the user to push.
As you can see, organizing all those checks and tools on different files helps me to locate problems, and remember where I saved/stored that function/variable. Of course, abstracting everything into functions allows me to re-use those functionalities in several pieces of the code, avoiding dupplicates, or using different checks for the same feature.
I’m not going to talk about Notifications API. I’m not going to talk about how/when to create notifications so you get more engagement, neither. What I want to tell you is about the system I created on my webbapp to create notifications.
Right now, I have 3 different types of messages for the user:
- Inline messages. These messages are inline with the webapp content, and the most common example is a validation error on a form.
- Sticky messages. These messages are like ‘sticky bubbles’ attached to one of the window’s corners. They are more appreciable than inline messages, but they are not so invasive as real Notifications. A common example for using this is a message with ‘Your data was updated’.
- Native notification. Yes, classic notifications. A very invasive message that pops up in your screen, even when you are looking any other thing.
I have these 3 types of messages because each of them respond to a particular need. I’m not going to create a Notification just for saying your form failed, because you are in the form, so I don’t need to get your attention. Of course, I’m not going to show a sticky message for telling you that someone commented in your post, because maybe you are not looking my webapp, so you’d miss that event, that is really interesting to you.
So, what I made was to create a notifier module. This module is the one that renders sticky messages, so is always loaded and it has a ‘position: fixed’. The module will analyze any HTTP response and look for a ‘notifications’ or ‘errors’ property on the response.
Now imagine that we have an HTTP response with 2 notifications on it. Notifier module will check any of them, and will generate a ‘sticky message’ or a ‘native notification’ based on a property (that I named channel).
Thanks to this, my webapp is agnostic about how to display any notification. Webapp is going to paint what server says. This means that tomorrow I can change the channel of a particular notification (in server), and webapp is going to work perfectly.
And just for those cases where I should display ‘inline messages’, the component that want’s to display those messages will listen to ‘notifications’ and ‘errors’, so they can render those messages in the middle of the content.
I hope you enjoyed this post, and I’m sorry if you expected something more technical. I really want to write something more tech, with few lines of code, but that’s something for the future.
If you are curious, you can check this repo where I’m applying all the things that I’ve talked about on this post!