Polling Apple Push Notification feedback service with Node.js
June 3, 2012 4 Comments
Apple assumes that your app polls the Push Notification Feedback Service to get information about App uninstalls so you can stop sending notifications to those devices. Service returns list of tokens with uninstall timestamps.
This is how you can do it with Node.js. First, check TLS Example and how to create SSL certs to understand basics of how to make secure SSL connection with Node.js.
First simple utility to convert binary data to hex
function bintohex(buf) { var hexbuf = new Buffer(buf.length * 2); function nibble(b) { if (b <= 0x09) return 0x30 + b; return 0x41 + (b - 10); } for(var i=0; i < buf.length; i++) { hexbuf[i*2] = nibble(buf[i] >> 4); hexbuf[i*2+1] = nibble(buf[i] & 0x0F); } return hexbuf.toString('ascii', 0, hexbuf.length); }
Then define function that will be called with push device tokens
function processTokens( pdtokens ) { // send or process tokens here pdtokens.forEach( function( pd ){ console.log('TOKEN ' + pd.token ' INVALIDATED AT ' + new Date( pd.timestamp ) ); }); }
Then polling function that does it all, note that it assumes function connectAPN that needs simply create SSL connection to ‘feedback.push.apple.com’ port 2196 with your apps client cert and private key.
function pollAPNFeedback() { console.log("Connecting APN feedback service"); connectAPN(function( apnc ) { var bufferlist = []; apnc.on('data', function(data) { // APN feedback starts sending data immediately on successful connect bufferlist.push(data) }); apnc.on('end', function() { console.log("APN Connection closed"); // tokens are parsed to object var pdtokens = [] // concatenate all asynchronously collected buffers, and parse the PDU's from them var parsebuf; var totall = 0; for(var i=0; i < bufferlist.length; i++) { totall += bufferlist[i].length; } parsebuf = new Buffer(totall); var offset = 0; for(var i=0; i < bufferlist.length; i++) { bufferlist[i].copy(parsebuf, offset, 0); offset += bufferlist[i].length; } var count = 0; for(var k = 0; k < parsebuf.length; ) { count++; // parse timestamp var ts = ((parsebuf[k] << 24) + (parsebuf[k+1] << 16) + (parsebuf[k+2] << 8) + parsebuf[k+3]) >>> 0; k += 4; var l = ((parsebuf[k] << 8) + parsebuf[k]) >>> 0; k += 2; var pdtokenbuf = new Buffer(l); parsebuf.copy(pdtokenbuf, 0, k, k + l); k += l; var hextoken = bintohex(pdtokenbuf); pdtokens.push({timestamp: ts, token:hextoken}); // process tokens in 100 item batches if( count >= 100 ) { processTokens( pdtokens ); count = 0; pdtokens = []; } } processTokens(pdtokens); setTimeout(pollAPNFeedback, 1000*60*60*4); }); } pollAPNFeedback();
The function read raw binary data from connection and after Feedback services closes the socket (this happens immediately after it has sent the data) it parses tokens and calls processing function to handle them. Function connects to server every 4 hours.
Note that feedback service host and port is different for sandbox (testing environment) that also needs its own SSL certificates.
Hi,
Thanks for sharing, but I found some glitches in the article,
> for(var i=0; i > 4);
> hexbuf[i*2+1] = nibble(buf[i] & 0x0F);
> }
did you miss something here?
and,
> setTimeout(pollAPNFeedback, 1000*60*4);
it’s 4 minutes indeed, not 4 hours.
Regards.
Thanks for pointing those out, the code was indeed botched on copy-paste. Should be ok now.
Would you be able to show an example of if you wanted to send notifications to only specific phones? I have an app where users filter what push notifications they receive, and the info is stored on the server, but I haven’t been able to parse where the specific phones are identified in this code.
Pingback: Simple test server for Apple push notification and feedback integration | Brave New Method