{"id":2827,"date":"2016-09-30T21:10:11","date_gmt":"2016-09-30T21:10:11","guid":{"rendered":"http:\/\/www.nikola-breznjak.com\/blog\/?p=2827"},"modified":"2016-09-30T21:21:39","modified_gmt":"2016-09-30T21:21:39","slug":"cordova-plugin-voip-push-notifications","status":"publish","type":"post","link":"https:\/\/nikola-breznjak.com\/blog\/javascript\/ionic\/cordova-plugin-voip-push-notifications\/","title":{"rendered":"Cordova plugin for VoIP push notifications"},"content":{"rendered":"<h2>TL;DR<\/h2>\n<p>This plugin didn&#8217;t exist, so I <a href=\"https:\/\/www.npmjs.com\/package\/cordova-ios-voip-push\">made it<\/a> &#8211; Cordova plugin for receiving VoIP push notifications on <strong>iOS 8.0+ only<\/strong>.<\/p>\n<h2>Installation<\/h2>\n<p>For Ionic:<\/p>\n<p><code>ionic plugin add cordova-ios-voip-push<\/code><\/p>\n<p>For Cordova:<\/p>\n<p><code>cordova plugin add cordova-ios-voip-push<\/code><\/p>\n<h2>Usage<\/h2>\n<pre><code>var push = VoIPPushNotification.init();\n\npush.on('registration', function(data) {\n    log(\"[Ionic] registration callback called\");\n    log(data);\n\n    \/\/data.deviceToken;\n    \/\/do something with the device token (probably save it to your backend service)\n});\n\npush.on('notification', function(data) {\n    log(\"[Ionic] notification callback called\");\n    log(data);\n\n    \/\/ do something based on received data\n});\n\npush.on('error', function(e) {\n    log(e);\n});\n<\/code><\/pre>\n<p>Please see the Ionic demo for the exact usage example.<\/p>\n<h2>Running the demo<\/h2>\n<h3>Ionic setup<\/h3>\n<p>Clone this repo:<\/p>\n<p><code>git clone https:\/\/github.com\/Hitman666\/cordova-ios-voip-push.git<\/code><\/p>\n<p>CD into the cloned project and the Ionic demo project:<\/p>\n<p><code>cd cordova-ios-voip-push &amp;&amp; cd ionicDemo<\/code><\/p>\n<p>Install the dependencies:<\/p>\n<p><code>npm install &amp;&amp; bower install<\/code><\/p>\n<p>Install the platform and plugins (please note that this process may take a while to complete):<\/p>\n<p><code>ionic state reset<\/code><\/p>\n<p>Add the plugin (either one of three options would work):<\/p>\n<p><code>ionic plugin add cordova-ios-voip-push<\/code><\/p>\n<p>or<\/p>\n<p><code>ionic plugin add ..\/thePlugin\/VoIPPushNotification<\/code><\/p>\n<p>or like this:<\/p>\n<p><code>ionic plugin add https:\/\/github.com\/Hitman666\/cordova-ios-voip-push.git<\/code><\/p>\n<p>Prepare the project:<\/p>\n<p><code>ionic prepare ios<\/code><\/p>\n<p>Open the project in XCode by going into <code>platforms\/ios<\/code> and opening up the <code>pluginTest.xcodeproj<\/code> file.<\/p>\n<h3>XCode setup<\/h3>\n<p>If you don&#8217;t have an AppID and VoIP push certificate created in your <a href=\"https:\/\/developer.apple.com\/account\/\">Apple developer account<\/a>, you can do so by following my instructions from the <a href=\"http:\/\/www.nikola-breznjak.com\/blog\/ios\/create-native-ios-app-can-receive-voip-push-notifications\/\">How to create a native iOS app that can receive VoIP push notifications<\/a> tutorial.<\/p>\n<p><em>Take your time to do that an then come back, I&#8217;ll wait.<\/em><\/p>\n<p>To use the VoIP push in the app, you need to turn ON the <code>Background Modes<\/code> for your app and check few of the checkboxes:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/i.imgur.com\/U8Xcrlj.png\" alt=\"\" \/><\/p>\n<p>Make sure you select the following options:<\/p>\n<ul>\n<li>Audio, Airplay, and Picture in Picture<\/li>\n<li>Voice over IP<\/li>\n<li>Background fetch<\/li>\n<li>Remote notifications<\/li>\n<\/ul>\n<p>Next, you need to add PushKit framework to your project:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/i.imgur.com\/NdLMkC4.png\" alt=\"\" \/><\/p>\n<p>Also (<em>yeah, I know, a lot of setup<\/em>), you need to make sure that you set the appropriate Bundle Identifier. <em>You should have read about this in the <a href=\"http:\/\/www.nikola-breznjak.com\/blog\/ios\/create-native-ios-app-can-receive-voip-push-notifications\/\">tutorial<\/a> I linked above.<\/em><\/p>\n<p>After you run the project, in the XCode console you should see<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/i.imgur.com\/7NRFrUql.png\" alt=\"\" \/><\/p>\n<p>and on your device:<\/p>\n<p><img decoding=\"async\" src=\"http:\/\/i.imgur.com\/C25JdgQl.jpg\" alt=\"\" \/><\/p>\n<p>You can send VoIP pushes to yourself by editing the <code>simplepush.php<\/code> file (in the <code>pushSendingScript<\/code> folder from the Github repo). Just make sure you add your own device id that you&#8217;ll see show up on your phone. You have more options to send VoIP push, two of which you can read <a href=\"http:\/\/www.nikola-breznjak.com\/blog\/ios\/create-native-ios-app-can-receive-voip-push-notifications\/\">in the post<\/a> linked few times above.<\/p>\n<h2>!TL;DR<\/h2>\n<blockquote><p>\n  Disclaimer:<br \/>\n  This tutorial was inspired by the official <a href=\"https:\/\/github.com\/phonegap\/phonegap-plugin-push\/\">phonegap-plugin-push plugin<\/a>, and the fact that this kind of VoIP support didn&#8217;t exist. There were requests for it <a href=\"https:\/\/github.com\/phonegap\/phonegap-plugin-push\/issues\/728\">here<\/a>, <a href=\"http:\/\/stackoverflow.com\/questions\/39463120\/voip-push-notifications-in-ionic-for-ios\">here<\/a> and <a href=\"https:\/\/forum.ionicframework.com\/t\/voip-push-notifications-in-ionic-for-ios\/62417\/1\">here<\/a>. As well as on freelance sites like <a href=\"https:\/\/www.tapatalk.com\/topic\/1096945-freelancer-new-projects\/b3e5543b3c75263053eef202f8501735-cordova-plugin-for-voip-push-notifications-for-ios-for-ionic-app-by-knkk\">here<\/a>, <a href=\"https:\/\/m.freelancer.com\/projects\/Mobile-Phone\/Cordova-plugin-for-VoIP-push\">here<\/a> &#8211; hehe, hope these guys will send some donations to my favorite charity if they end up using this now \ud83d\ude09<\/p>\n<p>  Also, I don&#8217;t &#8216;do&#8217; ObjectiveC for a living (but I may change my mind after completing this :)), so I would really appreciate the constructive feedback in making this plugin better, so I look forward to your comments and potential pull requests!\n<\/p><\/blockquote>\n<p>In my <a href=\"http:\/\/www.nikola-breznjak.com\/blog\/ios\/create-native-ios-app-can-receive-voip-push-notifications\/\">previous tutorial<\/a> I showed how to create a native iOS app with Swift (ObjectiveC code also available) that can receive VoIP push notifications sent with Houston, custom PHP script or through Amazon SNS.<\/p>\n<p>In this tutorial I&#8217;m going to present to you the Cordova plugin that does the same thing; it allows hybrid iOS applications to receive the VoIP push notifications.<\/p>\n<h3>Building Cordova plugins<\/h3>\n<p>I won&#8217;t go into the details of Cordova plugin building in this document. If you&#8217;re new to building Cordova plugins, you can check out <a href=\"http:\/\/moduscreate.com\/writing-a-cordova-plugin-in-swift-for-ios\/\">this tutorial<\/a> and <a href=\"https:\/\/cordova.apache.org\/docs\/en\/latest\/guide\/platforms\/ios\/plugin.html\">official docs<\/a>, as they were indispensable in my quest to learn as much as possible about it.<\/p>\n<h4>plugin.xml<\/h4>\n<pre><code>&lt;?xml version='1.0' encoding='utf-8'?&gt;\n&lt;plugin id=\"com.nikola-breznjak.voippush\" version=\"1.0.0\" xmlns=\"http:\/\/apache.org\/cordova\/ns\/plugins\/1.0\" xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"&gt;\n    &lt;name&gt;VoIPPushNotification&lt;\/name&gt;\n    &lt;js-module name=\"VoIPPushNotification\" src=\"www\/VoIPPushNotification.js\"&gt;\n        &lt;clobbers target=\"VoIPPushNotification\" \/&gt;\n    &lt;\/js-module&gt;\n\n    &lt;platform name=\"ios\"&gt;\n        &lt;config-file target=\"config.xml\" parent=\"\/*\"&gt;\n            &lt;feature name=\"VoIPPushNotification\"&gt;\n                &lt;param name=\"ios-package\" value=\"VoIPPushNotification\" \/&gt;\n            &lt;\/feature&gt;\n        &lt;\/config-file&gt;\n\n        &lt;header-file src=\"src\/ios\/VoIPPushNotification.h\" \/&gt;\n        &lt;source-file src=\"src\/ios\/VoIPPushNotification.m\" \/&gt;\n    &lt;\/platform&gt;\n&lt;\/plugin&gt;\n<\/code><\/pre>\n<p>In this file you basically define:<\/p>\n<ul>\n<li>for which platform is this plugin (<code>&lt;platform name=\"ios\"&gt;<\/code>)<\/li>\n<li>where the source files of your plugin will be (<code>header-file<\/code> and <code>source-file<\/code> elements)<\/li>\n<li>where is the JavaScript file that will be the bridge from Cordova to native code (<code>js-module<\/code> tag <code>src<\/code> property)<\/li>\n<li>what will be the plugins name by which you&#8217;ll reference it in the Cordova\/Ionic code (<code>&lt;clobbers target=\"VoIPPushNotification\" \/&gt;<\/code>)<\/li>\n<\/ul>\n<h4>www\/VoIPPushNotification.js<\/h4>\n<p>This file, stripped of it&#8217;s comments is very short (75 LOC):<\/p>\n<pre><code>var exec = cordova.require('cordova\/exec');\n\nvar VoIPPushNotification = function() {\n    this._handlers = {\n        'registration': [],\n        'notification': [],\n        'error': []\n    };\n\n    \/\/ triggered on registration and notification\n    var that = this;\n    var success = function(result) {\n        if (result &amp;&amp; result.registration === 'true') {\n            that.emit('registration', result);\n        }\n        else if (result) {\n            that.emit('notification', result);\n        }\n    };\n\n    \/\/ triggered on error\n    var fail = function(msg) {\n        var e = (typeof msg === 'string') ? new Error(msg) : msg;\n        that.emit('error', e);\n    };\n\n    \/\/ wait at least one process tick to allow event subscriptions\n    setTimeout(function() {\n        exec(success, fail, 'VoIPPushNotification', 'init');\n    }, 10);\n};\n\nVoIPPushNotification.prototype.on = function(eventName, callback) {\n    if (this._handlers.hasOwnProperty(eventName)) {\n        this._handlers[eventName].push(callback);\n    }\n};\n\nVoIPPushNotification.prototype.off = function (eventName, handle) {\n    if (this._handlers.hasOwnProperty(eventName)) {\n        var handleIndex = this._handlers[eventName].indexOf(handle);\n        if (handleIndex &gt;= 0) {\n            this._handlers[eventName].splice(handleIndex, 1);\n        }\n    }\n};\n\nVoIPPushNotification.prototype.emit = function() {\n    var args = Array.prototype.slice.call(arguments);\n    var eventName = args.shift();\n\n    if (!this._handlers.hasOwnProperty(eventName)) {\n        return false;\n    }\n\n    for (var i = 0, length = this._handlers[eventName].length; i &lt; length; i++) {\n        var callback = this._handlers[eventName][i];\n        if (typeof callback === 'function') {\n            callback.apply(undefined,args);\n        } else {\n            console.log('event handler: ' + eventName + ' must be a function');\n        }\n    }\n\n    return true;\n};\n\n\nmodule.exports = {\n    init: function(options) {\n        return new VoIPPushNotification(options);\n    },\n\n    VoIPPushNotification: VoIPPushNotification\n};\n<\/code><\/pre>\n<p>This code follows the structure of the <a href=\"https:\/\/github.com\/phonegap\/phonegap-plugin-push\/blob\/master\/www\/push.js\">phonegap-plugin-push<\/a> push.js.<\/p>\n<p>Once you call the <code>init<\/code> method, you get the new VoIPPushNotification object which then exposes the methods for listening to the <code>registration<\/code>, <code>notification<\/code> and <code>error<\/code> events.<\/p>\n<h4>src\/ios\/VoIPPushNotification.h<\/h4>\n<pre><code>#import &lt;Cordova\/CDV.h&gt;\n#import &lt;PushKit\/PushKit.h&gt;\n\n@interface VoIPPushNotification : CDVPlugin &lt;PKPushRegistryDelegate&gt;\n\n@property (nonatomic, copy) NSString *VoIPPushCallbackId;\n- (void)init:(CDVInvokedUrlCommand*)command;\n\n@end\n<\/code><\/pre>\n<p>Since we&#8217;re writing in ObjectiveC, we need to define the functions in the .h file. Here we do few things:<\/p>\n<ul>\n<li>import Cordova and PushKit<\/li>\n<li>add an instance property <code>VoIPPushCallbackId<\/code><\/li>\n<li>add an <code>init<\/code> function declaration<\/li>\n<\/ul>\n<h4>src\/ios\/VoIPPushNotification.m<\/h4>\n<pre><code>#import \"VoIPPushNotification.h\"\n#import &lt;Cordova\/CDV.h&gt;\n\n@implementation VoIPPushNotification\n\n@synthesize VoIPPushCallbackId;\n\n- (void)init:(CDVInvokedUrlCommand*)command\n{\n  self.VoIPPushCallbackId = command.callbackId;\n  NSLog(@\"[objC] callbackId: %@\", self.VoIPPushCallbackId);\n\n  \/\/http:\/\/stackoverflow.com\/questions\/27245808\/implement-pushkit-and-test-in-development-behavior\/28562124#28562124\n  PKPushRegistry *pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];\n  pushRegistry.delegate = self;\n  pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];\n}\n\n- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type{\n    if([credentials.token length] == 0) {\n        NSLog(@\"[objC] No device token!\");\n        return;\n    }\n\n    \/\/http:\/\/stackoverflow.com\/a\/9372848\/534755\n    NSLog(@\"[objC] Device token: %@\", credentials.token);\n    const unsigned *tokenBytes = [credentials.token bytes];\n    NSString *sToken = [NSString stringWithFormat:@\"%08x%08x%08x%08x%08x%08x%08x%08x\",\n                         ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),\n                         ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),\n                         ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];\n\n    NSMutableDictionary* results = [NSMutableDictionary dictionaryWithCapacity:2];\n    [results setObject:sToken forKey:@\"deviceToken\"];\n    [results setObject:@\"true\" forKey:@\"registration\"];\n\n    CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:results];\n    [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; \/\/[pluginResult setKeepCallbackAsBool:YES];\n    [self.commandDelegate sendPluginResult:pluginResult callbackId:self.VoIPPushCallbackId];\n}\n\n- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type\n{\n    NSDictionary *payloadDict = payload.dictionaryPayload[@\"aps\"];\n    NSLog(@\"[objC] didReceiveIncomingPushWithPayload: %@\", payloadDict);\n\n    NSString *message = payloadDict[@\"alert\"];\n    NSLog(@\"[objC] received VoIP msg: %@\", message);\n\n    NSMutableDictionary* results = [NSMutableDictionary dictionaryWithCapacity:2];\n    [results setObject:message forKey:@\"function\"];\n    [results setObject:@\"someOtherDataForField\" forKey:@\"someOtherField\"];\n\n    CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:results];\n    [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];\n    [self.commandDelegate sendPluginResult:pluginResult callbackId:self.VoIPPushCallbackId];\n}\n\n@end\n<\/code><\/pre>\n<p>In the implementation file we define our functions. The first one is <code>init<\/code>, and here we basically register for VoIP push notifications by using <code>PKPushRegistry<\/code>.<\/p>\n<p>Then, to satisfy that, we implement two functions: <code>didUpdatePushCredentials<\/code> and <code>didReceiveIncomingPushWithPayload<\/code>.<\/p>\n<p>The first one is triggered with the device token, which we then send back to the &#8216;JS world&#8217;. We listen for this in our Ionic\/Cordova apps and send this information to our backend so that later when we&#8217;re going to send the push we know to which device id.<\/p>\n<p>The second one is triggered when we receive the actual VoIP push notification. In this example, we take out the message (<code>@alert<\/code>) and return it to the &#8216;JS world&#8217;. In my particular case, this can be a message with a certain keyword, so that then I know what to do in my app based on that keyword.<\/p>\n<p>Again, I have to stress here that you may wanna alter this to your liking and process even more data which you receive through the VoIP push notification.<\/p>\n<h2>Conclusion<\/h2>\n<p>I hope this plugin will come handy to you. Also, I hope that this will give you enough information so that you&#8217;ll be dangerous enough to go and fiddle with the code yourself.<\/p>\n<p>As I&#8217;ve mentioned before, I would really appreciate the constructive feedback so that we can make this plugin better, so I look forward to your comments and optional pull requests.<\/p>\n<p>Btw, I updated these posts with this solution:<\/p>\n<ul>\n<li><a href=\"http:\/\/stackoverflow.com\/questions\/39463120\/voip-push-notifications-in-ionic-for-ios\">StackOverflow post<\/a><\/li>\n<li><a href=\"https:\/\/forum.ionicframework.com\/t\/voip-push-notifications-in-ionic-for-ios\/62417\">Ionic framework forum<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/phonegap\/phonegap-plugin-push\/issues\/728\">phonegap-plugin-push plugin<\/a><\/li>\n<\/ul>\n<p>and I have also asked the main maintainer of <a href=\"https:\/\/github.com\/phonegap\/phonegap-plugin-push\/\">phonegap-plugin-push plugin<\/a> if this would make sense to put as a PR on that plugin.<\/p>\n<p>I will update you on the progress of all this, and till then &#8211; code on!<\/p>\n<blockquote class=\"twitter-tweet\" data-width=\"550\">\n<p lang=\"en\" dir=\"ltr\"><a href=\"https:\/\/twitter.com\/hashtag\/Cordova?src=hash\">#Cordova<\/a> plugin for <a href=\"https:\/\/twitter.com\/hashtag\/VoIP?src=hash\">#VoIP<\/a> push notifications <a href=\"https:\/\/t.co\/jkn56YWGTU\">https:\/\/t.co\/jkn56YWGTU<\/a><\/p>\n<p>&mdash; Nikola Bre\u017enjak (@HitmanHR) <a href=\"https:\/\/twitter.com\/HitmanHR\/status\/781964805951324160\">September 30, 2016<\/a><\/p><\/blockquote>\n<p><script async src=\"\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>TL;DR This plugin didn&#8217;t exist, so I made it &#8211; Cordova plugin for receiving VoIP push notifications on iOS 8.0+ only. Installation For Ionic: ionic plugin add cordova-ios-voip-push&hellip;<\/p>\n","protected":false},"author":1,"featured_media":2829,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[43],"tags":[],"class_list":["post-2827","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ionic"],"_links":{"self":[{"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/posts\/2827","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/comments?post=2827"}],"version-history":[{"count":5,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/posts\/2827\/revisions"}],"predecessor-version":[{"id":2835,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/posts\/2827\/revisions\/2835"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/media\/2829"}],"wp:attachment":[{"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/media?parent=2827"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/categories?post=2827"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nikola-breznjak.com\/blog\/wp-json\/wp\/v2\/tags?post=2827"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}