My question is similar to this: Register event not getting called on ios #386
Everything works fine on my android device, but on ios it seems to be that the registration part will be skipped (I get no error or something):
Code:
var push = PushNotification.init({
android: {
senderID: "XXXXXX"
},
ios: {
senderID: "XXXXXX",
alert: "true",
badge: "true",
sound: "true",
gcmSandbox: "true"
},
windows: {}
});
push.on('registration', function(data) {
console.log(data.registrationId);
});
push.on('notification', function(data) {
console.log(data.message)
// data.title,
// data.count,
// data.sound,
// data.image,
// data.additionalData
});
push.on('error', function(e) {
// e.message
});
Some information what I'm using:
cordova: 5.0.0
platforms: android 4.0.0, ios 4.0.0
EDIT
I also use this simple notify.js to send my notification to the different devices (works fine for android):
var gcm = require('node-gcm');
var message = new gcm.Message();
var registrationIds = [];
// Value the payload data to send...
message.addData('message',"Test");
message.addData('title','Hello world' );
message.addData('notId','2');
//message.addData('soundname','beep.wav');
message.collapseKey = 'demo';
//message.delayWhileIdle = true; //Default is false
//message.timeToLive = 1000;// Duration in seconds to hold in GCM and retry before timing out. Default 4 weeks (2,419,200 seconds) if not specified.
// Set up the sender with you API key
//iOS
var sender = new gcm.Sender('API Key iOS');
//Android
//var sender = new gcm.Sender('API Key android');
// Add the registration tokens of the devices you want to send to
//ios:
registrationIds.push('token ios');
//android
//registrationIds.push('token android');
// Send the message
// ... trying only once
sender.sendNoRetry(message,registrationIds, function(err, response) {
if(err) console.error(err);
else console.log(response);
});
// ... or retrying
sender.send(message, registrationIds, function (err, response) {
if(err) console.error(err);
else console.log(response);
});
// ... or retrying a specific number of times (10)
sender.send(message, registrationIds, 10, function (err, response) {
if(err) console.error(err);
else console.log(response);
});
EDIT2
I finally get this error message:
{"multicast_id":xxxxxxxxxxxxxxxxx,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"}]}
I had the same issue. I fixed it by enabling push notification in xcode capabilities.
Make sure you have the latest version.
Related
I am facing an issue with a react native app in production. Some request fails and other go through. The issue happens to a lot of users and it's random. Only happens to our ios app and not our android app. The device has internet because we have #react-native-community/netinfo logging every time the network changes and in almost all the cases is not logging any change (there are some times that the user losses internet in which case it's fine the network error).
We use sentry to log app errors. In the image below you can see the device makes a post request to our API and very quickly it goes to response interceptor error.
In the tags image you can see that the problem only occurs on release 5.3.3 but I don't know what is causing this from this release. I have not changed anything from axios request.
Since I don't have a reputation of 10 I cannot post images but here are the links.
Network Issue Report: https://i.postimg.cc/PJWjvCvt/Screen-Shot-2022-06-16-at-11-25-06-AM.png
Image of sentry tags: https://i.postimg.cc/Y0NSBr7s/Screen-Shot-2022-06-16-at-11-43-37-AM.png
This is our axios interceptor:
const axiosInstance = axios.create({
baseURL: ENV.API_URL,
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
});
axiosInstance.interceptors.request.use(async function (config) {
config.headers['device-app-version'] = DeviceInfo.getVersion();
try {
const token = await AsyncStorage.getItem('token');
if (token) {
config.headers['authorization'] = "JWT " + token;
}
} catch (error) {
console.log("ERROR GET ASYNCSTORAGE DATA FAILED ON TOKEN", error);
}
const httpMetric = firebase.perf().newHttpMetric(config.url, config.method.toUpperCase());
config.metadata = { httpMetric };
await httpMetric.start();
return config;
}, function (error) {
console.log("Request interceptor error");
console.log(error);
return Promise.reject(error);
});
axiosInstance.interceptors.response.use(async function (response) {
const { httpMetric } = response.config.metadata;
if (response.status != 200) {
Sentry.withScope(function(scope) {
scope.setTag('API', response.config.url);
scope.setExtra('API', response.config.url);
scope.setFingerprint(['API', response.config.url]);
Sentry.captureException(response);
});
}
httpMetric.setHttpResponseCode(response.status);
httpMetric.setResponseContentType(response.headers['content-type']);
await httpMetric.stop();
return response;
}, async function (error) {
const { httpMetric } = error.config.metadata;
console.log("Response interceptor error");
console.log(error);
console.log("error.response");
console.log(error.response);
const scope = new Sentry.Scope();
if (error.response) {
// Request made and server responded
console.log(error.response.data);
console.log(error.response.headers);
console.log("Status:", error.response.status);
if (error.response.status == 403) {
return Promise.reject(error);
}
scope.setTag('API', error.response.config.url);
scope.setExtra('API', error.response.config.url);
scope.setFingerprint(['API', error.response.config.url]);
Sentry.captureException(error.response, scope);
} else if (error.request) {
// The request was made but no response was received
console.log(error.request);
Sentry.captureException(error.request, scope);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
Sentry.captureException(error.message, scope);
}
httpMetric.setHttpResponseCode(error.response.status);
httpMetric.setResponseContentType(error.response.headers['content-type']);
await httpMetric.stop();
if (!error.response) {
error.response = {
data: [],
status: 401,
message: 'Error ocurred making request. Try again'
}
}
return Promise.reject(error);
},
);
Package.json (affected packages)
"axios": "^0.26.1",
"react": "17.0.2",
"react-native": "0.66.1",
I have an iOS app that sends a time sensitive push notification from an event via Cloud Firestore-triggered function.
In my function, I do a simple get operation prior to sending out the push notification, and it gets delivered from 30 sec up to 1 min. Can anyone advise on improving the speed?
I've looked at the online documentation talking about setting a minimum # of instances to reduce a cold start. I've also looked at this SO answer. As someone that's new to this, I admittedly am having difficulty following and pinpointing my advised next step. Any help and guidance would be extremely appreciated.
exports.pushNotification = functions.firestore.document('/users/{userId}').onUpdate((change, context) => {
var recipientUid = context.params.userId;
return admin.firestore().collection('users').doc(recipientUid).get().then(doc => {
const userData = doc.data();
var fcmToken = userData.fcmToken;
var message = {
notification: {
title: sendingTitle,
body: sendingMessage
},
apns : {
payload : {
aps : {
badge : 1,
sound: "default"
}
}
},
token: fcmToken
};
admin.messaging().send(message)
.then((response) => {
console.log('Successfully sent push notification message:', response);
return;
})
.catch((error) => {
console.log('Error sending message:', error);
return;
});
})
});
One possible cause is that you don't correctly manage the life cycle of your Cloud Function: as a matter of fact you are not returning the Promises chain, as shown below (see the comments in the code):
exports.pushNotification = functions.firestore.document('/users/{userId}').onUpdate((change, context) => {
var recipientUid = context.params.userId;
return admin.firestore().collection('users').doc(recipientUid).get()
.then(doc => {
const userData = doc.data();
var fcmToken = userData.fcmToken;
var message = { ... };
return admin.messaging().send(message) // !!! See the return here
}) // And see also that we close the then block here
.then((response) => {
console.log('Successfully sent push notification message:', response);
return;
})
.catch((error) => {
console.log('Error sending message:', error);
return;
});
});
For more details on how to correctly manage the life cycle, I would suggest you watch the 3 videos about "JavaScript Promises" from the Firebase video series as well as read the following doc. Also, reading the doc on how to chain Promises will help.
I am developing iOS app with Phonegap.
I am implementing the process which incrementing badge number when app receives push notification usijng phonegap-plugin-push.
On the server side which sending push notification,
the badge number is not configured.
I want to implement the process when app receives push notification,
app counts the current badge number, increments it and set the badgenumber.
Using push.getApplicationIconBadgeNumber and push.setApplicationIconBadgeNumber,
below my code works well when app is Foreground,
But it doesn't work when app is Background, Suspended、Not running.
var app = {
// Application Constructor
initialize: function() {
this.bindEvents();
},
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
onDeviceReady: function() {
console.log('Received Device Ready Event');
console.log('calling setup push');
console.log('platform : '+device.platform);
if ((device.platform == 'iOS') || (device.platform == 'Android')) {
app.setupPush();
}
},
setupPush: function() {
console.log('calling push init');
var push = PushNotification.init({
"android": {
"senderID": "xxxxxxxxxx"
},
"browser": {},
"ios": {
"sound": true,
"vibration": true,
"badge": true
},
"windows": {}
});
push.on('registration', function(data) {
console.log('registration event: ' + data.registrationId);
var oldRegId = localStorage.getItem(key);
console.log('oldRegId : ' + oldRegId);
if (oldRegId !== data.registrationId) {
console.log('different ID');
localStorage.setItem(key, data.registrationId);
}
});
push.on('error', function(e) {
console.log("push error = " + e.message);
});
push.on('notification', function(data) {
console.log('notification event');
navigator.notification.alert(
data.message, // message
null, // callback
data.title, // title
'Ok' // buttonName
);
push.getApplicationIconBadgeNumber(function(count) {
console.log('get badge : ' + count);
count++;
push.setApplicationIconBadgeNumber(function() {
console.log('set badge : ' + count);
}, function() {
console.log('set badge error');
}, count);
}, function() {
console.log('get badge error');
});
});
}
};
On this site,
https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/API.md#pushfinishsuccesshandler-errorhandler-id---ios-only
push.setApplicationIconBadgeNumber is explained
"Set the badge count visible when the app is not running", and
push.getApplicationIconBadgeNumber is explained
"Get the current badge count visible when the app is not running".
But this my code, both 2 function works when app is Foreground,
and the badge number is not changed when app receives push notification on background state.
So, is the explanation written on that site wrong?
App works the same, nevertheless the push notification option "content-available" is configured 1 or not on the push notification sending server side.
So, please tell me what the code should I write.
-- Versions --
crodova version : 7.1.0
platform iOS : 4.5.4
phonegap-plugin-push : 1.10.6
I resolved it by myselr.
But, badge number is not updated when app states is "Not runnnig".
I supposes it is not possible to update badge number when app states is "Not runnnig".
So, it would be the specification of this app.
If you want to update badge number when app states is "Not running",
I think you can realize it with define badge number at the sending server side.
Below steps can resolve my problem.
*** 1 : Using cordova-plugin-background-fetch
Write below in config.xml.
<plugin name="cordova-plugin-background-fetch" source="npm" />
*** 2 : Enable "Background Refresh" of this app
For iPhone, you can enable it with
tapping "Settings -> General -> Background App Refresh".
*** 3 : Configure content-available=1 & id to sending data.
Below is the example of the sending data.
{ \"aps\": { \"alert\": \"Hello World\",\"content-available\": 1 }, \"id\" : 3 }
In this example, id is configured to 3.
This id is used at push.finish function (See next section).
*** 4 : Add push.finish function
Add this function into "push.on('notification', function(data){});".
The name of id of data.additionalData is corresponding to that of id of the sending data.
(Checking the specification of push.finish function at official site)
push.on('notification', function(data) {
console.log('notification event');
navigator.notification.alert(
data.message, // message
null, // callback
data.title, // title
'Ok' // buttonName
);
push.getApplicationIconBadgeNumber(function(count) {
//alert('get badge : ' + count);
console.log('get badge : ' + count);
count++;
push.setApplicationIconBadgeNumber(function() {
console.log('set badge : ' + count);
}, function() {
console.log('set badge error');
}, count);
}, function() {
console.log('get badge error: ' + count);
});
//alert('notId: ' + data.additionalData.notId);
console.log('notification id: ' + data.additionalData.id);
// do something with the push data
// then call finish to let the OS know we are done
push.finish(function() {
console.log("processing of push data is finished");
}, function() {
console.log("something went wrong with push.finish for ID =", data.additionalData.id)
}, data.additionalData.id);
});
I am trying to handle push notifications on iOS.
My simple code looks something similar to this:
var Cloud = require("ti.cloud");
var deviceToken = null;
var deviceToken = Ti.App.Properties.getString('deviceToken');
Ti.App.iOS.registerUserNotificationSettings({
types: [
Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
]
});
Ti.App.iOS.addEventListener('usernotificationsettings', function registerForPush() {
Ti.App.iOS.removeEventListener('usernotificationsettings', registerForPush);
Ti.Network.registerForPushNotifications({
success: function(e) {
if (e.deviceToken !== Ti.App.Properties.getString('deviceToken', null)) {
deviceToken = e.deviceToken;
Ti.App.Properties.setString('deviceToken', deviceToken)
subscribeToChannel();
} else {
Ti.API.info('Already registered for push notifications!');
}
},
error: function(e) {
Ti.API.error('Failed to register for push notifications: ' + e.error);
},
callback: receivePush
});
});
function subscribeToChannel () {
Cloud.PushNotifications.subscribeToken({
device_token: deviceToken,
channel: 'general',
type: Ti.Platform.name == 'android' ? 'android' : 'ios'
}, function (e) {
alert(e.success === true ? 'Subscribed' : 'Error!');
});
}
// When receieve interactive remote notification
Ti.App.iOS.addEventListener('remotenotificationaction', function(e) {
alert('remotenotificationaction: ' + JSON.stringify(e));
});
// When receieve interactive notification in the background
Ti.App.iOS.addEventListener('localnotificationaction', function(e) {
alert('localnotificationaction');
});
// When receieve interactive notification in the foreground
Ti.App.iOS.addEventListener('notification', function(e) {
alert('notification');
});
function receivePush(e) {
alert('receivePush');
}
For the most part everything works fine. The following happens when I send a remote push notification:
When the app is in the background, a notification appears. Upon clicking the notification, I get the "receivePush" message, as expected
When the app is in the foreground, a notification does not appear, but I still get the "receivePush" message, as expected.
However, when I receive a notification while the app is in the background, and then click on the app directly (i.e. not clicking the notification), none of the above events is triggered!
How can I make sure an event is triggered for the last case.
I don't think this is possible since your callback function is assigned with a notification behavior, not app starting. This is not a Titanium problem but a workflow misunderstanding if you know what I mean.
I think for you is best to always check something when the app starts, not related to notifications.
I have spent lot of time to find correct cordova plugin for parse push notifications for both Android & iOS platforms.
My requirements are:
To receive parse push notification (in both android & iOS)
Able to store all the incoming push notifications in mobile local storage Sqlite.
I have tried all the below parse push cordova plugins for both Android & iOS platforms.
https://github.com/avivais/phonegap-parse-plugin
https://github.com/taivo/parse-push-plugin
https://github.com/campers/parse-push-plugin
https://github.com/manishiitg/parse-push-plugin
For Android: All the above plugins are working perfectly to fulfill my above mentioned requirements.
For iOS: Only 1st plugin i.e https://github.com/avivais/phonegap-parse-plugin is working. And that too i was not able to save the notifications in local storage sqlite. That means only my 1st requirement is fulfilled but not my 2nd requirement.
All the github pages of remaining plugins (i.e 2nd, 3rd, 4th) states that:
"Please note that I've only worked on the Android aspect of this fork. The iOS side is not yet up to date."
Is there any plugin which will work for both Android & iOS platforms to fulfill my 2 requirements?
(or)
If there is no common plugin for both the platforms, then how can I store the incoming plugins in iOS sqlite?
Please help me. Thanks in advance.
I happen to maintain https://github.com/taivo/parse-push-plugin
It looks like you caught my fork at its infancy. I picked it up when the upstream fork seemed stagnant for a while and at that time I was only addressing the Android aspect. Since then I've provided full iOS support. And it works for parse-server as well as the out-going parse.com. I also did one better and made installation just a matter of
cordova add https://github.com/taivo/parse-push-plugin
and writing a few config.xml tags to indicate server url, and app id.
That should take out the big pain of manually messing with Android Manifest, Java, and Objective C when setting up the plugin.
It should now meet or exceed your requirement. To receive push notification and store in sqlite, all you have to do is set an event handler in javascript. Be sure to wrap it with some sort of device ready or platform ready event handler to ensure the plugin has properly loaded.
$ionicPlatform.ready(function(){
if(window.ParsePushPlugin){
ParsePushPlugin.on('receivePN', function(pn){
console.log('yo i got this notif:' + JSON.stringify(pn) );
//
// do your sqlite storage here
//
});
}
});
You just might be interested in the Azure Push Notifications. It combines both Push notification services so you can send messages to both devices from one central point.
I quote:
Notification Hubs A scalable, cross-platform solution for sending push
notifications to mobile devices, Notification Hubs works well with
Cordova apps. Notification Hubs manages the registrations with each
PNS. More important, Notification Hubs lets you create template
registrations so you can send messages to all registered devices,
regardless of platform, with only a single line of code. You can also
use tags to send targeted notifications only to devices with specific
registrations. For more information about Notification Hubs, see the
Azure Web site at aka.ms/nkn4n4.
Here i have a helper class for registering your device with the pushnotification service. For sending push notifications, you can use an azure portal and send styled push notifications in json format.
var Pushman = {
Initialize: function (hubConnString, hubName, gcmSenderId, callbackRegistered, callbackUnRegistered, callbackInlineNotification, callbackBackgroundNotification, callbackError) {
//store connection and callback information on app startup for Push Registration later
Pushman.HubConnectionString = hubConnString;
Pushman.HubName = hubName;
Pushman.GcmSenderId = gcmSenderId;
//callbacks
Pushman.RegisteredCallback = callbackRegistered;
Pushman.UnRegisteredCallback = callbackUnRegistered;
Pushman.NotificationForegroundCallback = callbackInlineNotification;
Pushman.NotificationBackgroundCallback = callbackBackgroundNotification;
Pushman.ErrorCallback = callbackError;
},
RegisterForPushNotifications: function (tags) {
//setup Azure Notification Hub registration
Pushman.Hub = new WindowsAzure.Messaging.NotificationHub(Pushman.HubName, Pushman.HubConnectionString, Pushman.GcmSenderId);
Pushman.Hub.registerApplicationAsync(tags).then(Pushman.onRegistered, Pushman.onError);
//setup PushPlugin registration
Pushman.Push = window.PushNotification;
var push;
//register depending on device being run
if (device.platform == 'android' || device.platform == 'Android' || device.platform == "amazon-fireos") {
//android
push = Pushman.Push.init(
{ "android": { "senderID": Pushman.GcmSenderId } }
);
push.on('registration', Pushman.onRegistered);
push.on('notification', Pushman.onAndroidNotification);
push.on('error', Pushman.onError);
} else {
//iOS
push = Pushman.Push.init(
{ "ios": { "alert": "true", "badge": "true", "sound": "true" } }
);
push.on('registration', Pushman.onRegistered);
push.on('notification', Pushman.onIOSNotification);
push.on('error', Pushman.onError);
}
},
UnRegisterForPushNotifications: function () {
if (Pushman.Hub != null) {
//dont pass through error handler
//unreg azure
Pushman.Hub.unregisterApplicationAsync()
.then(Pushman.onUnRegistered, null);
//unreg native
Pushman.Push.unregister(Pushman.onUnRegistered, null);
}
},
onRegistered: function (msg) {
Pushman.log("Registered: " + msg.registrationId);
//only call callback if registrationId actually set
if (msg.registrationId.length > 0 && Pushman.RegisteredCallback != null) {
Pushman.RegisteredCallback(msg);
}
},
onUnRegistered: function () {
Pushman.log("UnRegistered");
if (Pushman.UnRegisteredCallback != null) {
Pushman.UnRegisteredCallback();
}
},
onInlineNotification: function (msg) {
Pushman.log("OnInlineNotification: " + msg);
if (Pushman.NotificationForegroundCallback != null) {
Pushman.NotificationForegroundCallback(msg);
}
},
onBackgroundNotification: function (msg) {
Pushman.log("OnBackgroundNotification: " + msg);
if (Pushman.NotificationBackgroundCallback != null) {
Pushman.NotificationBackgroundCallback(msg);
}
},
onColdStartNotification: function (msg) {
Pushman.log("OnColdStartNotification: " + msg);
if (Pushman.NotificationBackgroundCallback != null) {
Pushman.NotificationBackgroundCallback(msg);
}
},
onError: function (error) {
Pushman.log("Error: " + error);
if (Pushman.ErrorCallback != null) {
Pushman.ErrorCallback(error);
}
},
onAndroidNotification: function (e) {
switch (e.event) {
case 'registered':
if (e.regid.length > 0) {
Pushman.onRegistered("Registered");
}
break;
case 'message':
if (e.foreground) {
//if this flag is set, this notification happened while app in foreground
Pushman.onInlineNotification(e.payload.message);
} else {
//otherwise app launched because the user touched a notification in the notification tray.
if (e.coldstart) {
//app was closed
Pushman.onColdStartNotification(e.payload.message);
}
else {
//app was minimized
Pushman.onBackgroundNotification(e.payload.message);
}
}
break;
case 'error':
Pushman.onError(e.msg);
break;
default:
Pushman.onError("Unknown message");
break;
}
},
onIOSNotification: function (event) {
//TODO: not sure how ios works re cold start vs inline msg types?
if (event.alert) {
navigator.notification.alert(event.alert);
}
if (event.badge) {
Push.setApplicationIconBadgeNumber(app.successHandler, app.errorHandler, event.badge);
}
},
tokenHandler: function (result) {
// iOS - not sure its use though appears somewhat important
// Your iOS push server needs to know the token before it can push to this device
// here is where you might want to send it the token for later use.
alert('device token = ' + result);
},
log: function (msg) {
console.log(msg);
},
}
///"class" variables - not sure how to put them into the js "class"
Pushman.Push = null;
Pushman.Hub = null;
Pushman.HubConnectionString = null;
Pushman.HubName = null;
Pushman.GcmSenderId = null;
Pushman.NotificationForegroundCallback = null;
Pushman.NotificationBackgroundCallback = null;
Pushman.RegisteredCallback = null;
Pushman.UnRegisteredCallback = null;
Pushman.ErrorCallback = null;
I did not write this myself, all credit goes to this guy.
Then you just need to initialize the plugin when the application starts:
//azure notificationshub connection information
notificationHubPath = "notificationhub name";
connectionString = "notificatin hub connectionstring";
//sender id for google cloud services
var senderIdGCM = "sender id from google gcm";
//tag registration (csv string), can be empty but not undefined
var registrationTagsCsv = ""; //test1, test2
var app = {
Initialize: function () {
//reg for onload event
this.AppStart();
},
AppStart: function () {
"use strict";
document.addEventListener('deviceready', app.onLoad, false);
document.addEventListener('deviceready', onDeviceReady.bind(this), false);
function onDeviceReady() {
// Handle the Cordova pause and resume events
document.addEventListener('pause', onPause.bind(this), false);
document.addEventListener('resume', onResume.bind(this), false);
// TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
};
function onPause() {
// TODO: This application has been suspended. Save application state here.
};
function onResume() {
// TODO: This application has been reactivated. Restore application state here.
};
},
onLoad: function () {
app.log("Initializing...");
//setup push notifications
Pushman.Initialize(connectionString, notificationHubPath, senderIdGCM,
app.onNotificationRegistered, app.onNotificationUnRegistered,
app.onNotificationInline, app.onNotificationBackground, app.onNotificationError);
//hookup cmd buttons
app.registerForPush();
//$("#register").click(app.registerForPush);
//$("#unregister").click(app.unRegisterForPush);
app.onAppReady();
},
registerForPush: function (a, c) {
app.log("Registering...");
//register for tags
Pushman.RegisterForPushNotifications(registrationTagsCsv);
},
unRegisterForPush: function (a, c) {
app.log("UnRegistering...");
//register for tags
Pushman.UnRegisterForPushNotifications();
},
onAppReady: function () {
app.log("Ready");
},
onNotificationRegistered: function (msg) {
app.log("Registered: " + msg.registrationId);
},
onNotificationUnRegistered: function () {
app.log("UnRegistered");
},
onNotificationInline: function (data) {
app.log("Inline Notification: " + data);
},
onNotificationBackground: function (data) {
app.log("Background Notification: " + data);
},
onNotificationError: function (error) {
app.log("Error: " + error);
},
log: function (msg) {
console.log(msg);
},
};
If you want to store the messages then you just need to add your code for storing to sql where the messages get received. You'll need an azure account to make this work, here you can get a free trail. It will allow you to send up to 1 million push notifications a month free of charge.
I think this article may be of use, it has more of a direct native workaround for your hybrid app to work
http://www.hiddentao.com/archives/2015/04/10/parse-push-notifications-for-your-android-and-ios-cordova-app/.
I'm working on a Cordova android app, and this seems to be a working solution