Push

Push helps you reach out to your users in a timely fashion with important messages. You can use it to inform users about updates to some piece of data, about new services available nearby or that they are winners in a game. Using Custom server code you can set up triggers which send push notifications to a targeted set of people based on changes in the data you specify.

Kinvey brings Android Push Notifications to you through an integration with Google Cloud Messaging (GCM) and iOS Push Notifications through an integration with AWS SNS. While push notifications can only be configured to be used by either iOS or Android devices, our APIs provide support for registering/unregistering devices and associating them with Kinvey users.

Setup

Console Setup

Android

  1. Navigate to the Firebase Console and follow the guide to create a new project or select an existing project.
  2. Click the gear icon in the top left of the side menu and select Project Settings.
  3. Click on the Cloud Messaging tab and write down the Sender ID and Server Key (formerly known as Project Number).
  4. On Kinvey's console select your App.
  5. Navigate to Engagement and select Push.
  6. Click Configure Push.
  7. In the Android section, fill in the Sender ID and Server Key fields with the respective Sender ID and Server Key values obtained in step 3.
  8. Click Save

iOS

When using Apple Push Notification Service (APNS) you will get two push certificates; one for development and one for production/ad-hoc. These certificates require push notifications to be sent to different servers depending on if your app is in development or production.

The production certificate is only usable if your app is in the App Store.

  1. Generate an Apple Push Certificate .p12 file for your app (instructions).
  2. After you export the .p12 file, on Kinvey's console navigate to Engagement and select Push.
  3. Click Configure Push.
  4. In the iOS section drag your .p12 file generated in step 1 where it says Drag here or click to upload a .p12 file.
  5. Click Save
  6. When you are ready to deploy your app, use your production/ad-hoc certificate. Export the .p12 file, and upload that to our service. Then select production as the certificate type and click Save. Deploying your application is a one-time action and cannot be undone.

Install and Setup Firebase Plugin

Install the nativescript-plugin-firebase for your app.

npm i --save nativescript-plugin-firebase

You will be prompted "Are you using this plugin ONLY as a Push Notification client for an external (non-Firebase) Push service? (y/n)". You will want to answer yes (y).

Follow the NativeScript Firebase plugin setup guide to complete setup for your application.

Important things to keep in mind are: - For NativeScript Angular apps you will then need to require (not import!) the plugin in app/main.ts. It should look like this https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/demo-ng/app/main.ts. - For regular NativeScript apps you will then need to require (not import!) the plugin in app/app.ts. It should look like this https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/demo-push/app/app.ts - Show your own consent screen before iOS requests permission, because a) the default popup (that'll also still be shown) isn't very friendly/configurable, and b) once the user denies permission they have to go to the settings app as you app can only request permission once.

Registering the Device

The device needs to be registered to receive push notifications through Kinvey. There must be a logged-in user when registering. This user will be linked to the device, allowing you to send targeted push notifications.

To register the device, initialize the Push module:

import * as Push from 'kinvey-nativescript-sdk/push';

const promise = Push.register((message: any) => {
  alert(message);
})
  .then((deviceToken: string) => {
    // ...
  })
  .catch((error: Error) => {
    // ...
  });
var Push = require('kinvey-nativescript-sdk/push');

var promise = Push.register((message: any) => {
  alert(message);
})
  .then(function(deviceToken) {
    // ...
  })
  .catch(function(error) {
    // ...
  });

The message callback is not strictly necessary. However, registering it here gives you the ability to compose push notifications with advanced options and data and is highly recommended, if only limited to the sendPayload() and broadcastPayload() sending methods.

For Android notifications, the message represents the message that appears in the notification drawer and has a predefined set of keys such as title and body. See the sendPayload() and broadcastPayload() method descriptions to learn how to set these. Read detailed information about the Google-defined keys in the Firebase Cloud Messaging documentation.

The following represents the object that the SDK will send to GCM after you set the payload using an SDK method.

{
  "message":{
    "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification":{
      "title":"Portugal vs. Denmark",
      "body":"The game is over"
    },
    "data":{
      "Score":"2:1",
      "Room":"PortugalVsDenmark"
    }
  }
}

For iOS notifications, the message represents the JSON dictionary required by APNs. It encompasses the aps dictionary, which contains Apple-defined keys, and any number of custom keys. See the sendPayload() and broadcastPayload() method descriptions to learn how to set these.

The alert, badge, and sound keys are default settings valid for all iOS notifications. Their values can be overridden on message level using the keys within the aps dictionary.

The following represents the object that the SDK will send to APNs after you set the payload using an SDK method.

{
    "aps" : {
        "alert" : {
            "title" : "Portugal vs. Denmark",
            "body" : "The game is over",
            "action-loc-key" : "PLAY"
        },
        "badge" : 5
    },
    "Score" : "2:1",
    "Room" : "PortugalVsDenmark"
}

Triggering Push Notifications

In addition to using the Console, you can trigger a push notification from Business Logic or a Flex service. This approach allows for greater flexibility as it makes it possible to schedule push notifications, to tie them down to various data events, and to set advanced options supported by Android and iOS.

You can send push notification from either collection hooks, effectively tying the notification to a data event, or custom endpoints which you can call manually or programmatically when needed.

The methods for sending push notifications are provided by the push module available in both Business Logic and the Flex SDK. They include send(), sendMessage(), sendPayload(), broadcastPayload(), and broadcastMessage(). Check the module's reference for detailed information.

The following code uses the onPreSave() before-processing collection hook to send a simple push notification to all users who fancy the winning team. Instead of saving any data to the data store, this request just causes the push notification to be sent.

function onPreSave(request,response,modules){
   var userCollection = modules.collectionAccess.collection('user')
   , utils = modules.utils
   , push = modules.push
   , template = '{{name}}, the {{team}} just won!'
   , pushedMessageCount = 0
   , userCount;

   // Find all users whose favoriteTeam matches
   // the incoming request's winning team.
   userCollection.findAsync({"favoriteTeam": request.body.winningTeam})
   .then(function(userDocs){
     // Total number of messages to send
     userCount = userDocs.length;

     // Each message is customized
     userDocs.forEach(function(doc){
       var values = {
          name: doc.givenName,
          team: doc.favoriteTeam
       };

       // Render the message to send
       var message = utils.renderTemplate(template, values);

       // Send the push
       push.send(doc, message);

       // Keep track of how many pushes are sent
       pushedMessageCount++;

       // Reduce the number of users left to push to
       userCount--;
       if (userCount <= 0){
         // All pushes sent out, complete the request
         response.body = {
             "message": "Attempted to push "
             + pushedMessageCount
             + " messages."
         };
         response.complete(200);
        }
     });
  })
  .catch(function(error){
      response.body = error;
      response.complete(400);
  });
}

The body of the request must have a winningTeam property. For example:

{
  "winningTeam": "Boston Red Sox"
}

Users are expected to have a givenName property and a favoriteTeam property.

The Push notification will be similar to:

Joe, the Kansas City Royals just won!

In case you want to set advanced message options such as title, body, badge, and sound or to send custom data to your app, you have to use the sending methods that accept advanced payloads. These include sendPayload() and broadcastPayload() (see API reference: Business Logic | Flex).

In your Business logic, you could have the following onRequest() handler which constructs the payloads for iOS and Android and then sends the new product message using sendPayload():

function onRequest(request, response, modules) {
    var push = modules.push;
    var users = [];
    var collectionAccess = modules.collectionAccess;
    var iOSAps = {
        alert: "We have a new product: Honey Bread! Tap to add to shopping list",
        badge: 2,
        sound: "notification.wav"
    }
    var iOSExtras = {Product: "Honey Bread", Price: 1.25, Quantity: 1};
    var androidPayload = {
        "_rawPayload": {
            "notification": {
                "title": "We have a new product - Honey Bread",
                "body": "Tap to add to shopping list",
                "color": "#444687"
            },
            "data": {
                "Product": "Honey Bread",
                "Price": 1.25,
                "Quantity": 1
            }
        }
    }
    collectionAccess.
        collection('user').
        find({ 'WantsNewProductsAlert': true }, function (err, res) {
            if (err) {
                response.complete(400);
            }
            else {
                push.sendPayload(
                    res,
                    iOSAps,
                    iOSExtras,
                    androidPayload,
                    function (err,  success) {
                        if (err) {
                            response.complete(200);
                        }
                        else {
                            response.complete(200);
                        }
                    }
                )
            }
        })
}

Handling Push Notifications

You may want to take further actions with the push notification when your app is brought to the foreground. To facilitate that, a pending event is available in the native part of your app for each received push notification.

To receive the event in your JavaScript code, you need to make the connection between the JavaScript code and the native part using the notificationCallback(). You should already have this defined in the registration code. It receives the arguments of the push notification event and makes them available in your JavaScript code.

The OS and the NativeScript Push Plugin react differently depending on the notification payload and the foreground/background state of the app. In some cases, the notification callback is not called. Check the NativeScript Push Plugin documentation to learn the details.

In the following example, the callback is used to process the data part of the notification which contains information about a new product. The app code then offers the user to save the product to their shopping list.

First, you need to register the callback as described in the Registering the Device section:

{
   //Android - specific settings
   android: {
       senderID: config.androidSenderId
   },
   //iOS - specific settings
   ios: {
       alert: true,
       badge: true,
       sound: true
   },
   notificationCallback: (message) => {
        var shoppingList = Kinvey.DataStore.collection('ShoppingList');
        shoppingList.save(JSON.parse(message))
            .then((res) => {
                //handle success
            })
            .catch((err) => {
                //handle error
            })
    }
};

In case you are sending a mixed notification that includes both message and data parts, you need to include the following handler in your application code. It makes sure the data is available inside the app code in case the user taps the notification when the app is in the background. You don't need to invoke application.on() when sending data-only messages.

application.on(application.resumeEvent, function (args) {
           if (args.android) {
               var act = args.android;
               var intent = act.getIntent();
               var extras = intent.getExtras();
               if (extras) {
                   var shoppingList = Kinvey.DataStore.collection('ShoppingList');
                   var product = {};
                   product.Product = extras.get('Product');
                   product.Price = extras.get('Price');
                   product.Quantity = extras.get('Quantity');
                   shoppingList.save(product)
                       .then((res) => {
                           //handle success
                       })
                       .catch((err) => {
                           //handle error
                       })
               }
           }
       });

Unregistering the Device

The unregister operation allows you to remove the current device from the user account's list of registrations, effectively stopping the user from receiving push notifications on the device. The operation is useful when the user has opted out of receiving this kind of notifications or for another reason. Unregister requires an active user. It will return an error if you call it after logging out the user.

import * as Push from 'kinvey-nativescript-sdk/push';

const promise = Push.unregister()
  .then(() => {
    // ...
  })
  .catch((error: Error) => {
    // ...
  });
var Push = require('kinvey-nativescript-sdk/push');

var promise = Push.unregister()
  .then(function() {
    // ...
  })
  .catch(function(error) {
    // ...
  });

NativeScript Angular

You can use the PushService in your Angular module to register/unregister the device with Kinvey for push notifications.

import { Component } from '@angular/core';
import { PushService } from 'kinvey-nativescript-sdk/angular/push';

@Component()
export class PushComponent {
  constructor(private pushService: PushService) {}

  async register() {
    try {
      const deviceToken = await this.pushService.register();
      console.log("You have successfully registered"
            + "device ${deviceToken} for push notifications with Kinvey.");
    } catch (error) {
      console.log(error.message);
    }
  }

  async unregister() {
    try {
      const deviceToken = await this.pushService.unregister();
      console.log("You have successfully unregistered"
            + "device ${deviceToken} for push notifications with Kinvey.");
    } catch (error) {
      console.log(error.message);
    }
  }
}