Meteor, Native iOS Push Notifications, Heroku, Raix:Push, Cordova

Arthur Carabott
Jun 21, 2016 · 7 min read

I spent a day of pain getting native iOS push notifications working with a Meteor/Cordova app. Meteor was chosen to quickly prototype a design concept, I mistakenly thought that adding notifications would be trivial, it ended up eating up a way bigger part of my day than it should have. I’m going to save you that time.

I only got this working for iOS notifications, although this provide pointers to get them working with the platforms supported by Raix:Push:

  • APN iOS
  • GCM Android
  • APN Safari web push (partially implemented)
  • GCM Chrome OS (partially implemented)
  • Firefox OS (partially implemented)

Disclaimer 1: I was building a prototype, I’m using a release candidate version of Raix:Push, this may not be production ready!

Disclaimer 2: Push notifications will only work in production, not development.

This guide draws on the work of others, but tries to bring them up to date and unify them, thanks to the work of Harry Ward, Jack Wu, and node-apn.

Meteor App

This guide works under the assumption that you have a working Meteor 1.3 App, and you’re looking to add notifications. If not, walk through the basic app tutorial.

Heroku / Hosting

I deployed my app to a free instance on Heroku, see this guide for how to set it up. That said, there isn’t much that is Heroku specific, so you should be fine using other hosting.


Apple Push Notifications

Push notifications will only work on a real device (not the simulator). You will also need to be a member of the Apple Developer Program.

Once you have an account and are logged in, go to Certificates, Identifiers & Profiles

  • Click App IDs
  • Click the + symbol in the top right
  • Give your App ID a description
  • Make sure Explicit App ID is selected
  • Add a Bundle ID in the reverse-domain style, e.g. com.fakeCompany.meteorApp
  • Under App Services make sure that Push Notifications is checked
  • Continue, Push Notifications should be Configurable
  • Click Register
  • Click Done
  • Click on your new App ID and press Edit
  • For both Development and Production press Create Certificate and follow the instructions

You should end up downloading two files aps_development.cer and aps.cer

Make a new folder e.g. ~/Desktop/certs and move both files there.

SSL Certificates

Open both aps_development.cer and aps.cer, this will open Keychain Access.app.

Search for the start of your Bundle ID (e.g. com.fakeCompany) this should bring up the new certificates.

For both Apple Development IOS Push Services and Apple Push Services items:

  1. Expand the certificate with the triangle
  2. Right-click the key and choose Export
  3. Save them to the same folder as your certificates (~/Desktop/certs/) naming them meteorApp-dev.p12 and meteorApp-prod.p12 for the Apple Development IOS Push Services and Apple Push Services keys respectively. You will be asked to add a passphrase, do this and remember it!

We are now going to generate our .pem files, thanks to the node-apn guide for this!

Open up a Terminal and run the following commands one at a time. For the second line of each pair you will have to enter the password you chose when exporting your keys.

$ cd ~/Desktop/certs$ openssl x509 -in aps_development.cer -inform DER -outform PEM -out meteorApp-cert-dev.pem
$ openssl pkcs12 -in meteorApp-dev.p12 -out meteorApp-key-dev.pem -nodes
$ openssl x509 -in aps.cer -inform DER -outform PEM -out meteorApp-cert-prod.pem
$ openssl pkcs12 -in meteorApp-prod.p12 -out meteorApp-key-prod.pem -nodes

You should now have a certificate/key pair for development and production. Test them with the following commands:

# Development
$ openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert meteorApp-cert-dev.pem -key meteorApp-key-dev.pem
# Production
$ openssl s_client -connect gateway.push.apple.com:2195 -cert meteorApp-cert-prod.pem -key meteorApp-key-prod.pem

If they are successful the session will stay open, the last few lines you will see are:

Verify return code: 0 (ok)
---

To exit the session type a few characters (e.g ‘hi’) and press enter, you should see

closed

If the test was unsuccessful the session will close automatically and you will see the ‘closed’ message without having to type anything. Go through the certificate steps again carefully if this is the case.

Note: If you check through the logs you may see this message

verify error:num=20:unable to get local issuer certificate
verify return:0

This isn’t an issue, but if you want to understand it, you can read this Stackoverflow answer, the quick ‘solution’ is to download the Entrust.net Certificate Authority (2048) from https://www.entrust.com/root-certificates/entrust_2048_ca.cer

Save it to the same folder as your certificates and run the commands as

# Development
$ openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert meteorApp-cert-dev.pem -key meteorApp-key-dev.pem -CAfile entrust_2048_ca.cer
# Production
$ openssl s_client -connect gateway.push.apple.com:2195 -cert meteorApp-cert-prod.pem -key meteorApp-key-prod.pem -CAfile entrust_2048_ca.cer

Now copy these four files to the private directory in the root of your meteor app (create it if it doesn’t exist).

Even though we are only going to use the production cert/key, I’ve shown you how to create the development ones in case you need them.

Expired Apple Certificate

If you bought your machine before February 14 2016 you will probably have to install the renewed Apple Worldwide Developer Relations Certification Intermediate. This is required for when we deploy our app to TestFlight.

  1. Download the new certificate from: https://developer.apple.com/certificationauthority/AppleWWDRCA.cer
  2. Double click it and Keychain Access.app will open
  3. Search for “World” and you should see your new certificate and two expired ones
  4. Right click each of the old ones and delete them

Raix:Push

I came across the raix:push plugin, that aims to provide a unified API for native push notifications (mobile and browser), in the release candidate version we will use it it wraps phonegap-plugin-push for mobile on the client side, and node-apn on the server side.

Warning: the documentation is out of date, and is not entirely clear how active the development is, but it has a fairly nice API, and it works (eventually). It took a fair bit of reading Github issues to figure this all out.

In the terminal navigate to the root directory of your app.

Install the latest release candidate version

$ meteor add raix:push@3.0.3-rc.5

mobile-config.js

We need to add a mobile-config.js if it doesn’t exist already

$ touch mobile-config.js

Which should have the following code

App.info({
id: 'com.fakeCompany.meteorApp', // replace with your bundle ID
name: 'MeteorApp' // replace with your own name
});
App.configurePlugin('phonegap-plugin-push', {
// Dummy value
// If using Google GCM you should use your app id instead
SENDER_ID: '12341234'
});

Server / Client code

We need to add a file for the client and server, I keep all import files in a directory called imports if the directories I use don’t exist, either create them or use your own, to be safe you can put client and server code in their respective folders in your app.

$ touch imports/client/push.js
$ touch imports/server/push.js

Client Code

Add the following code to imports/client/push.js

Push.Configure({
ios: {
alert: true,
badge: true,
sound: true,
clearBadge: true
}
});

For details on these options see this documentation.

Server Code

Add the following code to imports/server/push.js

Push.debug = true;Push.Configure({
apn: {
certData: Assets.getText('meteorApp-cert-prod.pem'),
keyData: Assets.getText('meteorApp-key-prod.pem),
passphrase: 'YOUR_KEY_PASSPHRASE',
production: true,
gateway: 'gateway.push.apple.com',
}
});
Push.allow({
send: (userId, notification) => {
// allow all users to send notifications
return true;
}
});
Meteor.methods({
'serverNotification'(title, text) {
Push.send({
title,
text,
from: 'server',
badge: 1,
query: {}
});
}
});

This code does four things:

  1. Enables debug mode, useful for getting set up but you will eventually want to disable it
  2. Configures the server to use our production certificate and key files, you need to add the passphrase you created when exporting your keys
  3. Enables all users to send notifications. You may want to change this in the future. You can also deny certain users from sending.
  4. Adds a Meteor method to easily test our notifications are working.

Note: This is just an example notification, which is sent to all users, this can be changed by using the query parameter, e.g.

query: {
userId: 'target-user-id'
}

Deployment

As mentioned, I’m using Heroku, so after the initial setup I can deploy with

$ git push heroku

It is very useful to view the logs by running

$ heroku logs -t

Build for iOS

To build for iOS you need to run the following command, using your heroku app URL:

$ meteor run ios-device --mobile-server https://your-url-here.herokuapp.com

This will start up Xcode and build your app. You then need to push this to TestFlight. Rather than cover this in detail, I recommend this excellent tutorial: https://www.raywenderlich.com/101790/ios-beta-testing-with-testflight-tutorial

Sending your test notification

  • Fire up a browser and visit your Heroku URL.
  • Open up the console and run
Meteor.call('serverNotification','the title','the text');

Wait a few seconds and you should get your notification! I’m currently investigating an issue with getting the title to display instead of the app name. This is good starting point, hopefully I’ll update when I either get this working or abandon!