Using the JavaScript SDK with cross-platform apps

A tutorial for adding the JavaScript SDK code to your app to allow your app users to receive push notifications from your Dotdigital account

Unlike our native mobile SDKs, the JavaScript SDK is for cross-platform apps, therefore, after you've added the JavaScript SDK code to your app, you must use a framework, such as Cordova (with PhoneGap) to get a native device token (registration ID), and send it to Dotdigital through the JavaScript SDK.

1588

JS framework sending the registration ID between Dotdigital and your app

To set up push notifications for cross-platform apps, complete the following tasks:

  1. Install the JavaScript SDK
  2. Configure the JavaScript SDK
  3. Initialise the JavaScript SDK
  4. Register the push tokens for the app
  5. Handle deep link callbacks

Sample Code

This tutorial explains how to use the SDK with JavaScript, but you can find a full sample of the TypeScript code in the 'JavaScript SDK sample code in TypeScript' section.

More sample apps including Cordova implementations can be found here, the Foundation SDK samples are the samples to look at:

Installing the JavaScript SDK

The JavaScript SDK can be installed from either NPM or Bower, depending on your intended usage.

📘

ES6 Promises

ES6 Promises are used in this SDK which are widely supported in web containers and browsers, but depending on which browsers you are targeting, you may need to use a polyfill such as: es6-shim

Classical JavaScipt

If you use classical JavaScript in your project, and you just want to include a script that exposes some global objects on your page, use Bower.

Bower

  1. Install the JavaScript SDK
$ bower install comapi-sdk-js-foundation
  1. Import the script into your project
<script src="bower_components/comapi-sdk-js-foundation/dist/comapi-foundation.js"></script>
//Minified version
<script src="bower_components/comapi-sdk-js-foundation/dist/comapi-foundation.min.js"></script>

ES6 JavaScript

If you use ES6 modules in your project, for example in the Angular and Ionic 2 frameworks, use NPM. The examples for the NPM package are in TypeScript because the NPM version of the SDK is written in TypeScript.

NPM

  1. Install the JavaScript SDK
$ npm install @comapi/sdk-js-foundation --save
  1. Import the Foundation, ComapiConfig, and IAuthChallengeOptions modules from the SDK file
import { Foundation } from "@comapi/sdk-js-foundation"
import { ComapiConfig } from "@comapi/sdk-js-foundation"
import { IAuthChallengeOptions } from "@comapi/sdk-js-foundation"

White listing the SDKs API calls

The built in security in Cordova based apps will restrict the URIs the Cordova app pages can access, therefore you will need to white list the URIs the SDK uses in order for it to operate. To do this do the following:

If using the cordova-plugin-whitelist

Ensure the following line is added to your config.xml file in your project:

<allow-navigation href="https://*.comapi.com/*" />

If using Content-Security-Policy tags

Ensure your Content-Security-Policy tags include the following directive:

connect-src https://api.comapi.com:*

For example:

<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;connect-src https://api.comapi.com:*">

Configuring the JavaScript SDK

Before you can configure the JavaScript SDK, you need the following:

  1. Create a ComapiConfig object by calling the Comapi.ComapiConfig() constructor
var comapiConfig = new COMAPI.ComapiConfig();
  1. Pass your API Space ID to the withApiSpaceId() method
var comapiConfig = new COMAPI.ComapiConfig()
    .withApiSpace(appConfig.apiSpaceId);
  1. Pass the function that creates a JWT to the withAuthChallenge() method
var comapiConfig = new COMAPI.ComapiConfig()
    .withApiSpace(appConfig.apiSpaceId)
    .withAuthChallenge(challengeHandler);

Initialising the JavaScript SDK

After you've configured the SDK, pass the comapiConfig object to the initialise() method.

This method creates a valid session by calling your JWT function and by using the JWT to create the user's profile ID (profileId string).

When a session is stopped and started again, a new JWT token is created and used to create a new profile ID.

A session is stopped in any of the following circumstances:

  • The user uninstalls the app, and then reinstalls it
  • The user clears all of the app's data
  • You invoke the endSession() method, but this should only be done if you want to stop push notifications being received or change the app user.

Your app needs a valid session in order to add an email address to the user's profile.

COMAPI.Foundation.initialise(comapiConfig)
    .then(function (sdk) {
        console.log("Foundation interface created", sdk);
    })
    .catch(function (error) {
        $log.error("paragonService: failed to initialise", error);
    });

Next in order to send push notifications to the device of your app users, you need to obtain a native device push token using a third-party framework and pass it to the SDK to use. This is covered in the next section.

Using a JavaScript framework to get a native device token and register them

Many frameworks, such as React Native, Cordova with the PhoneGap plugin, and Ionic Capacitor, can be used to acquire the native push tokens required to send push notifications from Dotdigital.

This tutorial uses the Cordova framework with the PhoneGap plugin, but any framework that you choose to use must be able to get a native device token (registrationId).

🚧

Please ensure your APNS (iOS) token is formatted correctly

Our SDK is expecting the APNS push token to be formatted as a 64 character hexadecimal string. Most of the push plugins for the frameworks will do this automatically, and your token should look similar to this: 02df25c845d460bcdad7802d2af6fc1dfce97283bf75cc993eb6dca835ea2e2f

If you token doesn't look like the example above then it is likely that you have simply cast the raw bytes returned by iOS for the push token to a string instead of a hexidecimal string!

If your app is for use on iOS, you'll need to have your app ID's bundle ID. This ID must match the value of the id attribute in the <widget> element of your config.xml file.

If your app is for use on Android, you'll need to have your app's package name that you entered in your push notification profile.

Getting a registration ID, using the Cordova framework with the Adobe PhoneGap plugin

Push notifications can be sent only if the registration ID (registrationId) is successfully passed to the JavaScript SDK.

Getting the registrationId (native push token) is an asynchronous task that is performed after the Cordova deviceready event. In the example below you can see using the Ionic Push library an event handler is created to store the native push token when Ionic Push receives one, and then register it with the Javascript SDK.

The app must set the registrationId (native push token) if possible when:

  • The platform.ready event fires e.g. each time the app opens
  • Whenever the chosen mobile frameworks push library fires an event indicating a registrationId (native push token) has been acquired

The JavaScript SDK expects the registration ID to be passed to different methods, depending on the operating system. Therefore, use the cordova device plugin to find out which platform the user is on.

🚧

Ensure you bundle id and package id are correct

It is important that your bundle id for iOS and package id for Google FCM match those configured in the respective development portal.

🚧

When setting Apple APNS tokens

Please ensure that the environment parameter when calling setAPNSPushDetails() is correct for the app build type, otherwise APNS will not work! The environment parameter should be:

  • For development builds - Set the parameter to Environment.development
  • For adhoc builds - Set the parameter to Environment.production
  • For production app store builds - Set the parameter to Environment.production
import { Environment } from "@comapi/sdk-js-foundation";
import { Push } from 'ionic-native';

const NATIVE_PUSH_TOKEN = "app.nativePushToken"; 

// Platform ready event handler
platform.ready().then(() => {
	// Initialise Ionic push plugin, or your equiavalent for your mobile framework
    let push = Push.init({
        //For Android apps
        android: { senderID: 'Your_Project_ID' },
        //For iOS apps
        ios: {
        alert: "true",
        badge: true,
        sound: 'false'
        }
    });

	// Register an event handler to handle when native push tokens are acquired async
    push.on('registration', (data) => {
		// Got a new native push token (registrationId)
        console.log("Got a native push token: ", data.registrationId);
		
		// Store the native token for reuse each launch
        localStorage.setItem(NATIVE_PUSH_TOKEN, data.registrationId);
		
		// Register the push token with the SDK
		registerNativeToken();
    });
  
	// Error handling for the push plugin
	push.on('error', (error) => {
		 console.error('Error with Push plugin', error);
	});
		
	// Your own push notfication handling for your app if required
    push.on('notification', (data) => {
          console.log("got a push notification", data);
    });
	
	// Try to register a native push token
	registerNativeToken();
}

function registerNativeToken() {
	// Retrieve the stored native push token (registrationId)
	let registrationId = localStorage.getItem(NATIVE_PUSH_TOKEN);
	
	// Skip if registrationId hasn't been collected yet
	if(registrationId){

		// There are separate methods to call, depending on the operating system
		if (platform.is('ios')) {
			// You need to create an APNs certificate in the Apple Developer Portal.
			// You must upload this cerificate to your push notification profile in Dotdigital
			// This certificate can be of type 'development' or 'production', hence the Environment parameter
			sdk.device.setAPNSPushDetails("<Bundle Id>", Environment.development, registrationId)
			.then(result => {
				console.log("setAPNSPushDetails() succeeded", result);
			})
			.catch(error => {
				console.error("setAPNSPushDetails() failed", error);
			});

		}else if(platform.is('android')){
			sdk.device.setFCMPushDetails("<Package Name>", registrationId)
			.then(result => {
				console.log("setFCMPushDetails() succeeded", result);
			})
			.catch(error => {
				console.error("setFCMPushDetails() failed", error);
			});
		}
	}else{
		// No native token
		console.log("Can't register native push token as no native push token available yet");
	}
}
const NATIVE_PUSH_TOKEN = "app.nativePushToken"; 

// Platform ready event handler
platform.ready().then(() => {
	// Initialise Ionic push plugin, or your equiavalent for your mobile framework
    let push = Push.init({
        //For Android apps
        android: { senderID: 'Your_Project_ID' },
        //For iOS apps
        ios: {
        alert: "true",
        badge: true,
        sound: 'false'
        }
    });

	// Register an event handler to handle when native push tokens are acquired async
    push.on('registration', (data) => {
		// Got a new native push token (registrationId)
        console.log("Got a native push token: ", data.registrationId);
		
		// Store the native token for reuse each launch
        localStorage.setItem(NATIVE_PUSH_TOKEN, data.registrationId);
		
		// Register the push token with the SDK
		registerNativeToken();
    });
  
	// Error handling for the push plugin
	push.on('error', (error) => {
		 console.error('Error with Push plugin', error);
	});
		
	// Your own push notfication handling for your app if required
    push.on('notification', (data) => {
          console.log("got a push notification", data);
    });
	
	// Try to register a native push token
	registerNativeToken();
}

function registerNativeToken() {
	// Retrieve the stored native push token (registrationId)
	let registrationId = localStorage.getItem(NATIVE_PUSH_TOKEN);
	
	// Skip if registrationId hasn't been collected yet
	if(registrationId){

		// There are separate methods to call, depending on the operating system
		if (platform.is('ios')) {
			// You need to create an APNs certificate in the Apple Developer Portal.
			// You must upload this cerificate to your push notification profile in Dotdigital
			// This certificate can be of type 'development' or 'production', hence the Environment parameter
			sdk.device.setAPNSPushDetails("<Bundle Id>", Environment.development, registrationId)
			.then(result => {
				console.log("setAPNSPushDetails() succeeded", result);
			})
			.catch(error => {
				console.error("setAPNSPushDetails() failed", error);
			});

		}else if(platform.is('android')){
			sdk.device.setFCMPushDetails("<Package Name>", registrationId)
			.then(result => {
				console.log("setFCMPushDetails() succeeded", result);
			})
			.catch(error => {
				console.error("setFCMPushDetails() failed", error);
			});
		}
	}else{
		// No native token
		console.log("Can't register native push token as no native push token available yet");
	}
}

Displaying push notifications

Displaying push notifications while the app is in the foreground

Push notifications are automatically displayed only while the app is in the background. These notifications are sent to the system tray (Android) or the Notification Center (iOS) and launch your app when users tap them.

If you want to display the push notification message while the app is in the foreground, do one of the following:

Android

For Android devices, you can choose to have push notifications displayed automatically when the app is in the foreground, or you can choose to display the push notification message yourself.

Displaying push notifications automatically

When you choose to have push notifications displayed automatically when the app is in the foreground, the on('notification') callback is run only when the user taps the notification. Therefore, you can decide what happens when the user taps the notification

The push notification is passed as an argument to the push.on('notification') callback.

  • On the init() method of the push plugin, set the android object's forceShow property to true (it's set to false by default).
import { Push } from 'ionic-native';

let push = Push.init({
        //For Android apps
        android: { senderID: 'Your_Project_ID',
                 	 forceShow: true
                 }
});
const push = PushNotification.init({
        //For Android apps
        android: { senderID: 'Your_Project_ID',
                 	 forceShow: true
                 }
});

Displaying push notifications yourself

If you want contol over how push notification messages are displayed, you can implement your own way of displaying the push notification while the app is in the foreground.

For this option, the on('notification') callback is run immediately, and the push notification is passed as an argument to it.

  • On the init() method of the push plugin, make sure the android object's forceShow property is set to false (it's set to false by default).

For iOS

You need to implement your own way of displaying the push notification while the app is in the foreground. The push notification is passed as an argument to the push.on('notification') callback.

JavaScript SDK sample code in TypeScript

// some app specific imports
import { AppSettings } from "../settings";
import { AuthService } from "./auth";

// Comapi class / interface imports
import { Foundation, ComapiConfig, IAuthChallengeOptions } from "@comapi/sdk-js-foundation"

export class ComapiService {

    public sdk: Foundation;

    private challengeHandler(options: IAuthChallengeOptions, answerAuthenticationChallenge) {
        this._authService.getToken(options.nonce)
            .then((token) => {
                answerAuthenticationChallenge(token);
            });
    }

    constructor(private _authService: AuthService) { }

    /**
     * Public method to encapsulate up the initialisation of Comapi 
     */
    public initialise(): Promise<Foundation> {

        return new Promise((resolve, reject) => {

            if (this._authService.isAuthenticated()) {

                let comapiConfig = new ComapiConfig()
                    .withApiSpace(AppSettings.APP_SPACE_ID)
                    // Note the this pointer binding so I can access this._authService in the authChallenge calllback
                    .withAuthChallenge(this.challengeHandler.bind(this));

                Foundation.initialise(comapiConfig)
                    .then((sdk) => {
                        this.sdk = sdk;
                        console.log("foundation interface created");
                        resolve(sdk);
                    })
                    .catch((error) => {
                        console.error("initialise failed", error);
                        reject(error);
                    });
            } else {
                reject("Not logged in");
            }
        });
    }
}

Handling deep links

To be able to consume a deep link in your Cordova based app you will need to install the following cordova plugin: cordova-plugin-dotdigital-pushutils. This can be installed by running the following command:

cordova plugin add @comapi/cordova-plugin-dotdigital-pushutils

This plugin facilitates retrieving the link information from the push notification and requesting the device to open it from native code. The link itself can either be a deep link into some area of your app or a normal URL pointing a web page.

Ensure your app implements the push.on event handler which will be invoked each time a push notification is received e.g.

push.on('notification', (data) => {
    // Is there a deep link to process?
    if (cordova && cordova.plugins && cordova.plugins.dotdigitalPlugin) {
        // You don't need to call containsLink,
        // it is safe to call handleLink with all notifications.
        // the method is provided should you want to apply some logic in your app.

        if (cordova.plugins.dotdigitalPlugin.containsLink(data)) {
            cordova.plugins.dotdigitalPlugin.handleLink(data)
                .then(() => {
                    console.log("Deep link handled 👍");
                })
                .catch((error) => {
                    console.log("Issue handling deep link 😌", error);
                });
        }
    }
});

Deep link support

Now you have the ability to send deep links via a push message you may wish to implement deep links within your own app to trigger actions.

It is your responsibility to ensure your app supports any deep links you send in a push message, or you may send deep links for alternate apps such as Facebook etc...

To do this you will require a plugin to manage your deep links custom URL schemes. We recommend using cordova-plugin-customurlscheme due to its simplicity, however there are many other plugins which will perform this functionality. You may want to select something more tailored to the framework that you are using. Other options include:

Example of how to implement deep link in Cordova

You should add the plugin to your app and specify a scheme you want your app listen to. The scheme can be any short alphanumeric value that doesn't clash with existing schemes:

cordova plugin add cordova-plugin-customurlscheme --variable URL_SCHEME=yourUrlScheme

This will set a deep link for yourUrlScheme:// scheme.

You can add this function to your app to handle interpreting the deep links:

function handleOpenURL(url) {
    setTimeout(function() {
      alert("received url: " + url);
      console.log(`handleOpenUrl(${url})`);
    }, 0);
}

To correctly handle deep links in your app you will need to tweak the native project files:

iOS

You will need to add LSApplicationQueriesSchemes key to your info.plist file to allow opening of deep links.

You can set this in the projects info tab following these steps:

  1. In your apps project in XCode, go to the Info tab. Under the URL Types, click the + button.
  2. Put the app’s bundle identifier (e.g., com.example.yourApp) in the Identifier field.
  3. In the URL Schemes field, put in your deep link scheme.

Android

You will need to modify AndroidManifest.xml file for your app. Apply the following changes:

  1. android:launchMode needs to be set to singleTask
  2. Add an intent filter for your deep link scheme:
<intent-filter>
  <data android:scheme="yourUrlScheme"/>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
</intent-filter>

Alternatively you could add the following configuration to your Cordova project config.xml file and the intent filter will be automatically created:

<platform name="android">
   <allow-intent href="yourUrlScheme:*" />
</platform>

👍

Next steps

Now ensure your app passes an email address to the SDK for the app user to ensure they get a contact created in Dotdigital by following these instructions

📘

Want to know more about the SDK?

To find out more about the SDK and it features and functions please go here