Firebase Cloud Messaging (FCM) and Cordova to provide reliable and free push notificaitons on iOS

4,210 views
Skip to first unread message

Jeremy

unread,
Jun 1, 2016, 11:46:01 AM6/1/16
to phonegap
Hi,

Apple APNS is not reliable and the user can disable push notifications for your app.

My app may receive data every few seconds from my server.  Instead of using AJAX to poll the server an kill the phone's battery and bandwidth, I discovered FCM which says it's:
"... the most popular cloud-to-device push messaging service in the world...Available for free and for unlimited usage, FCM supports messaging on iOS, Android, and the Web, and is heavily optimized for reliability and battery-efficiency."

But I can't find a Cordova plugin to use FCM.  So, I am considering using their web app route to integrate FCM into my iOS app via

Has anyone implemented FCM in their apps (i'm guessing since it's 3rd party that Android implementation would be the same as iOS) ?

Many thanks.


Simon MacDonald

unread,
Jun 1, 2016, 11:48:42 AM6/1/16
to phonegap
It's on my roadmap for a future release of the push plugin:

https://github.com/phonegap/phonegap-plugin-push/issues/929

Simon Mac Donald
http://hi.im/simonmacdonald
> --
> -- You received this message because you are subscribed to the Google
> Groups "phonegap" group.
> To post to this group, send email to phon...@googlegroups.com
> To unsubscribe from this group, send email to
> phonegap+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/phonegap?hl=en?hl=en
>
> For more info on PhoneGap or to download the code go to www.phonegap.com
> ---
> You received this message because you are subscribed to the Google Groups
> "phonegap" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to phonegap+u...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

jcesarmobile

unread,
Jun 2, 2016, 5:13:01 AM6/2/16
to phonegap
As far as I know, FCM will send the notifications to Apple APNs and the APNs will send it to your devices, so no improvement in that. If the user disables the Push Notifications he won't receive them.

And I don't think the web app route you want to pick will work on Cordova apps, it seems a way to send web push notifications to Chrome browser.

sami...@gmail.com

unread,
Jun 2, 2016, 5:13:32 AM6/2/16
to phonegap
@Simon 

+1

Jeremy

unread,
Jun 2, 2016, 6:07:11 AM6/2/16
to phonegap
How can the message be reliable if FCM forwads it to APNS!?  I thought that FCM doesn't use APNS at all...I'll google this now..

Jeremy

unread,
Jun 2, 2016, 10:22:26 AM6/2/16
to phonegap
According to https://developers.google.com/cloud-messaging/ios/start you need to have a APNS certificate for FCM to work.  So it seems like FCM does use the APNS to deliver notifications to the app!

So what's the point of FCM, just use regular APNS!?  And the service is not 100% reliable...

Very confusing!...Anyone can clarify why FCM claims to be reliable?

Simon MacDonald

unread,
Jun 2, 2016, 10:24:37 AM6/2/16
to phonegap
Um....marketing. I don't think any company is going to put "works most
of the time" up on a splash page.
Simon Mac Donald
http://hi.im/simonmacdonald


jcesarmobile

unread,
Jun 2, 2016, 10:40:09 AM6/2/16
to phonegap
The point of FCM is to have the push notifications unified
And using FCM you don't have to deal with the socket connection with the APNs (well, you can use http2 now)

Jeremy

unread,
Jun 2, 2016, 10:52:02 AM6/2/16
to phonegap
Ok unified messaging, great...By "don't have to deal with the socket connection with the APNs" you mean that APNS isn't used by FCM on iOS?

According to the video at https://firebase.google.com/docs/cloud-messaging/ the FCM server sends the message direct to the iOS device!..

jcesarmobile

unread,
Jun 2, 2016, 10:57:59 AM6/2/16
to phonegap
No, I mean, if you don't use FCM (or other 3rd party sender/server) you have to create your own server, and you have to send the push data to the APNs yourself, using a socket. (Well, as I said, since early this year you can now use http2 to communicate with the APNs).

I don't think the video demonstrates anything, I just think that they are not going to explain how the iOS push works internally on the video, I don't think there is a way to send push notifications to iOS devices without using APNs.

Jeremy

unread,
Jun 2, 2016, 11:08:29 AM6/2/16
to phonegap
I thought that the FCM client might open a websocket to the FCM server, therefore bypassing the need for APNs.

I am googling trying to find how FCM works on iOS. Once I can 100% confirm how it works, i'll post it here...

If it does use APNs, then i'll have to add my own websocket client and server on iOS for reliable messaging.

Simon MacDonald

unread,
Jun 2, 2016, 11:11:45 AM6/2/16
to phonegap
I promise you, writing your own implementation of a push service will
lead you to madness. I did that at another place I worked at. Also, I
wouldn't be surprised if Apple had rules against it.
Simon Mac Donald
http://hi.im/simonmacdonald


jcesarmobile

unread,
Jun 2, 2016, 11:39:47 AM6/2/16
to phonegap
If you have to upload your APNs certificate it's because they communicate with the APNs, why would they ask for it if they don't use it?

Even using your own websocket doesn't guarantee to be reliable, in fact, they are probably less reliable as the push notifications are received by the system even if the app is closed, while a socket requires that you app is open all the time to receive the communication.

Simon MacDonald

unread,
Jun 2, 2016, 11:55:09 AM6/2/16
to phonegap
Yeah, seriously Jeremy don't do it. The only reason we could do it at
my old company is we were building Android from AOSP and were able to
modify the OS to add system level services.
Simon Mac Donald
http://hi.im/simonmacdonald


Jeremy

unread,
Jun 3, 2016, 10:45:15 AM6/3/16
to phonegap
Hi Simon, so if I my app needs to check my server for new data every few seconds, what's the recommended, minimal impact on draining the phone's batter, reliable way to do it?

Simon MacDonald

unread,
Jun 3, 2016, 11:18:48 AM6/3/16
to phonegap
Well the recommendation would be "don't do that". Polling is very bad
on battery life and you should avoid it whenever you can. Every time
you make a network request the device needs to wake up the radio and
make the connection to the remote server. Depending on the connection
type you have this can be very expensive in terms of battery.

Push notifications, regardless of them being GCM or APNS, work by
keeping a persistent TCP connection open with the remote push service.
This allows your app to only make a connection to your server when it
is notified there is new data. As well push notifications further save
battery life by making this a system level function where many apps
can share the same connection.

Read up on https://developer.android.com/training/efficient-downloads/regular_updates.html#GCM

Not sure what you are trying to do with your app but if it is in the
background use push notifications not polling to get new data.

Simon Mac Donald
http://hi.im/simonmacdonald


Jeremy

unread,
Jun 3, 2016, 1:20:37 PM6/3/16
to phonegap
Thanks Simon, very informative.  But push notifications can be disabled on iOS and Apple will not accept any app that "depends" on push notifications being enabled! Also, APNS is not 100% reliable and it should only be used as a 'hint' to the user that "something" happened in the app.

So, besides push notifications, what's the best way to know of new data on the server?

Simon MacDonald

unread,
Jun 3, 2016, 2:41:44 PM6/3/16
to phonegap

Maybe explain your use case a bit more and I can give a better suggestion.

Simon

Jeremy

unread,
Jun 3, 2016, 5:20:22 PM6/3/16
to phonegap
Ok great - I have users logging into different lectures.  When sufficient users are logged-in, the lecture begins.  Each user is notified of other class-mates when they login to a lecture. There's no deadline for a lecture to start. If a user leaves the lecture, every other user in the same lecture is notified...

jcesarmobile

unread,
Jun 3, 2016, 5:31:58 PM6/3/16
to phonegap
On iOS you have the "silent push" that tell the app that there is an app update. They will arrive even if the user disabled the push notifications. But as they are "silent" the user won't see them.
This can be disabled too on the settings if the user turn off the background updates, but is less likely that the user will do that.

Jeremy

unread,
Jun 3, 2016, 6:02:58 PM6/3/16
to phonegap
Hi jcesarmobile, thanks for the reply...but if the user disables push notifications, then 'slient' pushes are also disabled.

jcesarmobile

unread,
Jun 3, 2016, 6:49:02 PM6/3/16
to phonegap
No, they are not disabled, but they have to be really silent, with content-available set to 1 and no alertsound, or badge

Rob Willett

unread,
Jun 4, 2016, 3:36:52 AM6/4/16
to phon...@googlegroups.com
Hi,

I'm following this thread with interest as we had a similar technical requirements. 

We started with polling, discovered that this destroyed the battery life, moved to push notifications and found some (different) technical issues with a number of push providers and then moved to WebSockets on a dedicated node.js server

We found WebSockets to work well for us BUT we already had quite a lot of backend infrastructure in place and adding in a WebSocket server was relatively easy. Setting the WebSockets server up was quite easy using node.js and feeding it data was quite easy through Beanstalk and Redis. 

We use WebSockets to push out regular updates to users, i.e. in our case we send traffic updates to users every X minutes. X is undefined but is normally between 3-8 minutes (ish on a good day with the moon in the right phase). 

We still use silent and non-silent push notifications on both IOS and Android to alert the user to certain events, but most of our regular traffic is send over WebSockets. Each update is around 40K uncompressed, 7K compressed. We still use push notifications as we have a very unusual edge use case which means web sockets aren't quite the full answer

Oddly enough, moving to WebSockets meant we dumped a whole load of client code out which greatly simplified the codebase (always good) and battery consumption become far more manageable.

Not sure of this helps, but it may be another option to consider.

Rob

--

Jeremy

unread,
Jun 4, 2016, 9:12:08 AM6/4/16
to phonegap
Hi jcesarmobile, my mistake - you are correct, silent notifications remain active when push notifications are disabled.  But I found this article http://stackoverflow.com/questions/30644343/is-silent-remote-notifications-possible-if-user-has-disabled-push-for-the-app that says silent notifications can also be disabled via Settings > Background App Refresh :(  

Also, Apple say that notifications are not 100% reliable and this article https://support.layer.com/hc/en-us/articles/202952364-Can-I-send-silent-push-notifications- says that you app may not always be woken up to process a silent push notification.

So, I don't see how business critical logic can rely on this mechanism...

Jeremy

unread,
Jun 4, 2016, 9:18:22 AM6/4/16
to phonegap
Hi Rob Willet, thanks for your reply.  It seems that adding WebSockets is the only way to manage business-critical notifications.  Any Cordova plugin or JS library that you recommend on the client?

Rob Willett

unread,
Jun 4, 2016, 9:59:01 AM6/4/16
to phon...@googlegroups.com
Jeremy,


I think the documentation is, errr... eclectic, but most of the information is there, just buried around a bit and sometimes difficult to find. 

Rob Willett

unread,
Jun 4, 2016, 10:13:05 AM6/4/16
to phon...@googlegroups.com
Whoops, pressed send too early.

As I was saying the documentation is difficult at times but the code works and seems to be reliable. Since we installed it we have had zero issues and to be honest we have forgotten its there. 

We pulled a load of polling code out and simplified our code a lot as a lot of the error checking that we were doing is handled elsewhere. 

The node.js server was pretty simple, we had issues as we use Perl a lot of the backend and the node.js/Perl*/beanstalk interface was pretty rubbish. We ended up using Redis as a intermediate buffer and all the problems went away.

We run our notification server on a very small dedicated server and it is creaking at the seams, we may have to move to two cores and 2GB of memory, so it does fit on small kit as well :)

Rob

* For those of you under the age of 30, Perl is an old, single threaded language from the early 90's. Its brilliant at cutting through text and data structures, almost as fast as C for some things and great for taking in 200MB of XML data at a pop, cutting it up, rebuilding JSON structures, running through high complex regexp and with no worries about async callback hell. I got myself an extension built to my house on the proceeds of a job to read an unknown tape off an unknown system using an unknown file system written by an unknown program for a known company whose name I cannot divulge. The customer needed it processed ASAP for a legal case. Perl was the solution as you could interface directly to the tape drive, read it and have a mass of raw binary data to work there. Nice job at the time and writing it in C would have taken week it not months

Jeremy

unread,
Jun 5, 2016, 5:19:02 AM6/5/16
to phonegap
If WebSockets is a permenant connection to the Node.js server, doesn't this keep the wireless radio active the entire time and also drain the device's battery?

Rob Willett

unread,
Jun 5, 2016, 5:29:49 AM6/5/16
to phon...@googlegroups.com
Jeremy,

We thought this as well when we looked into websockets, we searched and searched and couldn't find anything that was definitive.  From our logs


Also our experience has been very positive. We compared before and after battery life and it was significantly better wth websockets.io. We also compared battery life to similar apps and again were happy with the results.

YMMV but we think websockets have reduced our battery consumption by quite a lot. 

Rob

Rob Willett

unread,
Jun 5, 2016, 5:31:12 AM6/5/16
to phon...@googlegroups.com
Missed one of your points, websockets doesn't actually keep the wifi or 4G/3G connection actually running all the time. If it did then the battery would die far quicker, it seems to open a connection and leave it dormant. 

I have tried to get more information but haven't had the time to really dig into the details.

Rob

Jeremy

unread,
Jun 5, 2016, 7:31:17 AM6/5/16
to phonegap
I found it hard too to find out the Websocket mechanics!  Thanks so much for your info!

Rob Willett

unread,
Jun 5, 2016, 9:07:57 AM6/5/16
to phon...@googlegroups.com
I didn't delve into the mechanics of websockets either but just used them. They were easy to get going and a lot simpler than polling. Also for broadcasts they work well.

Best of luck.

Rob

Simon MacDonald

unread,
Jun 6, 2016, 4:16:26 PM6/6/16
to phonegap
We used websockets to implement our push service as well. It uses less battery than polling but I caution against doing this unless you have a real business requirement to do so.

Jeremy

unread,
Jun 20, 2016, 5:02:06 PM6/20/16
to phonegap
Hi Rob, I want to push messages from my backend app server to Node JS running socket.io.  How do you use Redis to feed non-socket originating data into socket.io?

Rob Willett

unread,
Jun 21, 2016, 2:48:01 AM6/21/16
to phon...@googlegroups.com
Jeremy,

We have a series of Beanstalkd queues that capture data from various upstream sources. We use Redis as the buffer for all the data sources. We used Redis as we found the JavaScript/Beanstalkd code to be highly unreliable and basically worthless otherwise we would have gone directly from Beanstalkd to socket.io.

In  hindsight having Redis as a buffer is actually really nice and gives us persistence. Its also rock solid. 

In answer to your dierct q

Rob Willett

unread,
Jun 21, 2016, 2:58:24 AM6/21/16
to phon...@googlegroups.com
Not sure what happened there, too early in the morning for me and I must have pressed the wrong button.

In answer to your direct question.

We have three stages 

1. We read in existing files when we start Redis up as Redis acts as a persistent store.

2. We setup redis to read from the beanstalkd queues. Once these queues are running, point 1 becomes moot.

3. We then send things out to our users. Looking at the code to do this specifically, it looks pretty simple code, we last updated it 7th march 2016 so its not something we touch very often (if at all).

We have a JSON structure to hold four different types of data we send down, output_short_json is JSON data field holding traffic updates, its around 40K of data, analytics_json is a load of analytics stuff we send down, around 30K once per day. jamcams and speedcams are simple GPS chunks of data sent down every 72 hours or so.

var redisConfig = {
"output_short_json": {
cache: "" ,
client: null ,
debug: false ,
verbose: true
} ,
"analytics_json": {
cache: "" ,
client: null ,
debug: false ,
verbose: true
} ,
"jamcams_json": {
cache: "" ,
client: null ,
debug: false ,
verbose: true
} ,
"speedcams_json": {
cache: "" ,
client: null ,
debug: false ,
verbose: true
}
};

The code is here (almost verbatim).

io.on('connection', function (socket) {
    var now = moment().format('HH:mm:ss DD/MM/YYYY');
    if (typeof socket.handshake.query.id !== "undefined")
        console.log("[" + now + "] Socket.io: Connection made socketId='" + socket.id + "' phoneId='" + socket.handshake.query.id + "'");
    else
        console.log("[" + now + "] Socket.io: Connection made socketId='" + socket.id + "'");

    allClients.push(socket);

    // On first connection we send ALL the cached files down to the client.
    for (var key in redisConfig)
    {
        // Send down ALL the cached files (if we have any) so the client is up to date.
        if (redisConfig[key].cache)
        {
            var now = moment().format('HH:mm:ss DD/MM/YYYY');
            if (redisConfig[key].debug)
                console.log("[" + now + "] Updating client with cached '" + key + "' cache=" + redisConfig[key].cache);
            else
                                console.log("[" + now + "] Updating client with cached '" + key + "'");
            io.emit(key , { Data: redisConfig[key].cache , queue: key });
        } else
        {
            console.log("[" + now + "] Updating client with cached '" + key + "' cache=EMPTY");
        }
    }

    console.log("");

    socket.on('output_short_json', function (valueName , setValueResult) {
        if (redisConfig["output_short_json"].cache)
        {
            setValueResult({
                success : true ,
                data : redisConfig["output_short_json"].cache
            });
        } else
        {
            setValueResult({
                success : false ,
                message : "Unable to retrieve file"
            });
        }
    });

    socket.on('disconnect', function () {
        var now = moment().format('HH:mm:ss DD/MM/YYYY');

    if (typeof socket.handshake.query.id !== "undefined")
        console.log("[" + now + "] Socket.io: disconnect socketId='" + socket.id + "' phoneId='" + socket.handshake.query.id + "'");
    else
        console.log("[" + now + "] Socket.io: disconnect socketId='" + socket.id + "'");

        console.log("[" + now + "] Got disconnect! " + socket.id);

        var i = allClients.indexOf(socket);
        allClients.splice(i, 1);
    });
});

Thats about it.

Rob

Jeremy

unread,
Jun 21, 2016, 9:43:05 AM6/21/16
to phonegap
Hi Rob, thanks so much for your incredibly detailed reply.  I have opted for a simpler solution to push messages to the socket io server.  I have added a web server to my node js code as well as the socket server.  My backend server posts a message to the web server which then figures out which socket/user to emit the message to.

Jeremy

unread,
Jun 27, 2016, 7:34:14 AM6/27/16
to phonegap
Hi Rob,

What is your disconnect/reconnect strategy regarding the ios app being in the background and everything being suspended?  Googling hasn't provided much insight.

Many thanks.

Rob Willett

unread,
Jun 27, 2016, 8:07:01 AM6/27/16
to phon...@googlegroups.com
We ignore it since the library automatically disconnects and reconnects as it comes in and out of background.

Simple

Rob

Jeremy

unread,
Jun 28, 2016, 6:09:02 AM6/28/16
to phonegap
How does socket io know the app has gone to the background/foreground automatically?

And messages meant for an app that is in the background won't receive the message.  Do you check on the server if the app's socket is closed and if so you cache it on the server until the app's socket reconnects and then receives any missed messages?

Many thanks for your time.

Rob Willett

unread,
Jun 28, 2016, 9:31:19 AM6/28/16
to phonegap
The server gets an error back when it tries to make a connection.

We gave no issues when the app is in background as it's suspended anyway and can't process anything unless you write a native piece of code.

Our app does a check when it comes back.out of background to get latest messages.

Rob
Reply all
Reply to author
Forward
0 new messages