I’m a huge fan of Firebase, so I’m very excited that Google Cloud Messaging [GCM] has been re-branded Firebase Cloud Messaging [FCM], which can be used for cross platform messaging.
It’s actually very simple to set this up on Android and IOS, but what’s more interesting is the web client. I was really hoping things got eaier in this department. Unfortunately, right off the bat when checking out FCM web messaging nothing much has changed yet, the FCM site provides a link to the older (but excellent) codelab which details how to implement this using service workers and the HTML5 Push API.
The problem is the Push API is a very quickly evolving standard with plenty of activity revolving around it, and the above article does an excellent job of describing the nuts and bolts of it all, but leaves out some important information that would drive more widespread Push API adoption IMHO:
- The blog post makes no mention of how to send payload data with the Push notification. When a push event is received, the blog post describes how to display a predefined notification, but not how to send custom data. This is probably because only Chrome 50+ supports Push payloads, and this requires encryption. But the guide doesn’t quite explain how to do this
- Which libraries are available to simplify the process?
I’m not discouraging anyone from trying this, it’s actually pretty easy, but there are quite a number of steps involved, as we can see below, not counting encrypting and sending Push API payloads:
Surely there must be some libraries to help us out?
Let me try to address these two issues with some notes from research I’ve done so far (suggestions for avenues of research are welcome!! see my contact page to contact me).
There isn’t too much to write about here. The most promising one I’ve found that automates registering the service worker and handles a lot of the background work for the Push API is Google’s own “Propel” library. It’s very simple to implement as you can see from the readme, and only requires some minimal manual work like getting an FCM/GCM key, adding a manifest and a minimal service worker file.
While the library does handle the service worker registration and subscription side of things, it doesn’t do anything (yet) to help with actually displaying the notifications. I suspect this is going to change very soon, it just needs to be documented properly. That being said, adding notifications manually to the service worker file is trivial.
All in all, the library definitely helps in getting you on your feet quickly and with less trouble
Custom Push data payloads and associated backend libraries
Ok, so we have push notifications working, but we want to send custom Push data payloads. As we previously mentioned, the payload needs to be encrypted before it’s sent to the client, meaning not even FCM would know what data is being passed through the data payload.
There are some excellent examples of how to achieve this in Mozilla’s service worker cookbook:
In those examples they use Marco’s web-push npm library for NodeJS. This works across Chrome and Firefox. There’a similar NPM library provided by Google themselves (web-push-encryption):
For the python lovers, we can use Mozilla’s pywebpush library:
Here’s a troubleshooting tip: almost all the libraries require you to send the endpoint subscription details to your server. The aforementioned Propel library shows an example of how to do this on their README. The above mentioned backend databases then use this information (which is basically a URL describing which endpoint to send the Push to, and two public keys that are used to encrypt the data). But, with the exception of Google’s we-push-encryption library, I was being returned with an HTTP 400 error: Unauthorized Registration.
Weird, especially when the data I passed into all libraries was the same. Looking at the code from the web-push-encryption library, it seems that google is in a transition phase and while Chrome returns an endpoint similar to this:
We actually need to change the endpoint URL to something like:
Note the difference in URLs. In the web-push-encryption library they simply replace the string, so until Chrome starts returning the new endpoint you need to do this manually.
Once this is done, you can send custom payloads via Push and read that custom data via a call to:
In the service worker file, under the ‘push‘ event listener.
UPDATE: regarding the above idiosyncrasy, got an update from a Chrome dev:
I recently had a problem (seems to be a very common one ) when building a hybrid HTML5 mobile app. As can be seen in this online demo (https://mobilehtml5.org/ts/?id=23), one can use the input html tag with
The original suggestion pointed towards an “Android Advanced Webview” by a company named “Delight”. It is an excellent piece of code:
And now clicking on the input tag brings up the native android chooser, allowing a user to upload an image they had already taken. Good, but not perfect. Ideally we allow the user to choose if they would like to upload a saved image they already took, or allow them to choose the “camera” option and take a picture on the spot. Out of the box, Android Advanced Webview doesn’t support this . Bummer.
But this solution reminded me of something similar I played with years ago: the Intel-funded Crosswalk Project. It has definitely matured and gotten a lot better since I last checked it out. It proved relatively simple to embed it in my native android framework by following their documentation:
Note: if you’re using Android Studio and maven/gradle, a much – very much – easier way of embedding Crosswalk can be found here:
That did the trick! Now clicking on the input tag gives the user a choice:
Almost there. In my case, clicking on the camera option didn’t do anything😦. After some debugging and logging the intents being passed around, I noticed the android subsystem complaining of “revoked permission” even though I had the appropriate CAMERA permission in my manifest file:
<uses-permission android:name=”android.permission.CAMERA” />
UPDATE: If you see only the “camcoder” option as in the screenshot above, adding the “android.permission.WRITE_EXTERNAL_STORAGE” resolved the issue
Since I’m using Android 6.x, the permission scheme has changed and now requires you to ask for user permission at runtime . Following that realization, the below did the trick: