We are pleased to announce that you can now send push notifications to mobile devices (iOS and Android) using LoopBack with the release of the loopback-push-notifications module. What’s LoopBack? It’s an open source mobile backend, powered by Node.js, perfect for building API servers.

In this blog, we’ll walk you through a round trip of push notifications between a LoopBack Node.js server and a demo iOS application. (If you are looking for how to get push notifications working in Android, check out this blog.) The server side is an instance of LoopBack with a simple web page to list device registration records and send out push notifications to selected devices. The client side is an iOS application that uses the LoopBack iOS SDK to register itself with the server and respond to push notifications. You can get the demo applications on Github from these links:

  • https://github.com/strongloop/loopback-push-notification/tree/master/example/server
  • https://github.com/strongloop/loopback-push-notification/tree/master/example/ios
  •  

    Example: the marketing dept wants to “push” a promotion

    To illustrate the end to end flow, let’s start with a simple scenario to understand what components and steps are involved. Let’s suppose the marketing department needs to send a promotion to all users on the company’s iPhone application in the United States. What APIs are needed on the server side?

  • Create the promotion message.
  • Find all users whose address is in the United States.
  • Find the iOS device tokens associated with the users found from step 2.
  • Send out notifications for each matching iOS device through Apple’s APNS.
  • LoopBack’s push notification service provides the facilities for the server-side to produce and send notifications to mobile devices. The models involved are:

  • Notification model: it encapsulates a notification object, including the application message and other mobile platform properties, such as expiry, badge, and sound.
  • User model: it manages the mobile users. We can query the user model to decide what users are the target of a given push notification.
  • Installation model: it manages installations of client application on mobile devices. This way, we can find out what devices are being used by applications and users. Other properties such as time zone and subscriptions also help the server side to decide if and when the notification should be sent.
  • Push providers: they are responsible to interact with the cloud services to send out notifications. For example, there is a provider that connects to Apple’s push notification service (APNS) for iOS devices.
  • Now that we understand that the server side can identify devices of interest and push notifications to these devices, what about the client applications? How do we enable an iOS application to support push notifications?

  • Provision an application with Apple and configure it to enable push notifications.
  • Provide a hook to receive the device token when the application launches and register it with the LoopBack server using the LBInstallation class.
  • Provide code to receive notifications, under three different mode for the mobile application: foreground, background, or offline.
  • Process notifications.
  • Later in this blog, we’ll show you the necessary code to make your application capable of receiving notifications.

    The demo server application

    Set up the push notification model

    The demo server is just a regular LoopBack instance. To support push notifications, we need to create a push model with a database based data source using the fooling code example. The database is used to load/store the corresponding application/user/installation models.

    var loopback = require('loopback');
    
    var app = loopback();
    var db = require('./data-sources/db');
    
    // Load & configure loopback-push-notification
    var PushModel = require('loopback-push-notification')(app, { dataSource: db });
    var Application = PushModel.Application;
    var Installation = PushModel.Installation;
    var Notification = PushModel.Notification;
    

     

    Sign up an client application with push settings

    To support push notifications, the mobile application needs to be registered with LoopBack so that we can have an identity for the application and corresponding settings for APNS.

    First, we need to load the certificate and key files into strings.

    var fs = require('fs');
    var path = require('path');
    
    // You may want to use your own credentials here
    exports.apnsCertData = readCredentialsFile('apns_cert_dev.pem');
    exports.apnsKeyData =readCredentialsFile('apns_key_dev.pem'); 
    
    //--- Helper functions ---
    function readCredentialsFile(name) {
     return fs.readFileSync(
       path.resolve(__dirname, 'credentials', name),
       'UTF-8'
     );
    }
    
    The Application model has APIs for the sign-up.
    
     Application.register('strongloop',
    
       config.appName,
       {
         description: 'LoopBack Push Notification Demo Application',
         pushSettings: {
           apns: {
               certData: config.apnsCertData,
               keyData: config.apnsKeyData,
             pushOptions: {
             },
             feedbackOptions: {
               batchFeedback: true,
               interval: 300
             }
           },
           gcm: {
             serverApiKey: config.gcmServerApiKey
           }
         }
       },
       function(err, app) {
         if (err) return cb(err);
         return cb(null, app);
       }
     );
    

    Configure the database

    By default, the demo application uses an in-memory database to store applications and installations. You can customize it by setting MONGODB environment variable to a URL that points to your MongoDB, for example:

    mongodb://127.0.0.1/demo.

    Start the demo application

    $ node app

    http://localhost:3010

    Send notifications

    For the purpose of demonstration, we create a custom endpoint for the AngularJS client to send out a dummy notification for the selected device:

    // Add our custom routes
    var badge = 1;
    app.post('/notify/:id', function (req, res, next) {
     var note = new Notification({
       expirationInterval: 3600, // Expires 1 hour from now.
       badge: badge++,
       sound: 'ping.aiff',
       alert: '\uD83D\uDCE7 \u2709 ' + 'Hello',
       messageFrom: 'Ray'
     });
     PushModel.notifyById(req.params.id, note, function(err) {
       console.log('pushing notification to %j', req.params.id);
     });
     res.send(200, 'OK');
    });
    

    LoopBack provides two Node.js methods to select devices and send notifications to them.

    • notifyById: Select a device by registratrion id and send a notification to it
    • notifyByQuery: Select a list of devices by the query (same as the where property for Installation.find()) and send a notification to all of them.

     

    Prepare your iOS application

    Add LoopBack iOS SDK as a framework

    Open your xcode project, select targets, under build phases, unfold ‘Link Binary with Libraries’, and click on ‘+’ to add LoopBack framework.

    LoopBack iOS SDK provides the LBInstallation and LBPushNotification classes to simplify the push notification programming for iOS applications.

    LBInstallation allows the iOS application to register mobile devices with LoopBack. LBPushNotification provides a set of helper methods to handle common tasks for push notifications.

    Respond to application launch

    When the iOS application is launched, it should initialize an instance of LBRESTAdapter for the SDK to communicate with the LoopBack server. If the application is launched by an offline notification, we can get the message and process it.

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
       self.settings = [self loadSettings];
    
       // Instantiate the shared LBRESTAdapter. In most circumstances, you'll do this only once; putting that reference in a
       // singleton is recommended for the sake of simplicity. However, some applications will need to talk to more than one
       // server - create as many Adapters as you need.
       self.adapter = [LBRESTAdapter adapterWithURL:[NSURL URLWithString:self.settings[@"RootPath"]]];
    
       // Reference to Push notifs List VC
    
       self.pnListVC = (NotificationListVC *)[[(UINavigationController *)self.window.rootViewController viewControllers]
                                              objectAtIndex:0];
    
       LBPushNotification* notification = [LBPushNotification application:application
                                            didFinishLaunchingWithOptions:launchOptions];
    
       // Handle APN on Terminated state, app launched because of APN
       if (notification) {
           NSLog(@"Payload from notification: %@", notification.userInfo);
           [self.pnListVC addPushNotification:notification];
       }
    
       return YES;
    }
    

    Respond to device tokens

    An instance of the iOS application should receive a device token. The application needs to register the device token with other information such as appId, userId, timeZone, or subscriptions with the backend so that push notifications can be initiated from LoopBack by querying the installation model. The iOS application receives the device token from the application:didRegisterForRemoteNotificationsWithDeviceToken method.

    __unsafe_unretained typeof(self) weakSelf = self;
    
       // Register the device token with the LoopBack push notification service
    
       [LBPushNotification application:application
    
    didRegisterForRemoteNotificationsWithDeviceToken:deviceToken
    
                               adapter:self.adapter
    
                                userId:@"anonymous"
    
                         subscriptions:@[@"all"]
    
                               success:^(id model) {
    
                                   LBInstallation *device = (LBInstallation *)model;
    
                                   weakSelf.registrationId = device._id;
    
                               }
    
                               failure:^(NSError *err) {
    
                                   NSLog(@"Failed to register device, error: %@", err);
    
                               }
    
        ];
    
       ...
    
    }
    

     

    If your app fails to register for push notifications, the following method will be invoked:

    - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
    
       // Handle errors if it fails to receive the device token
           [LBPushNotification application:application didFailToRegisterForRemoteNotificationsWithError:error];
    }
    

    Respond to received notifications

    If your app is already running when the notification is received, the notification message is made available in the application:didReceiveRemoteNotification: method through the userInfo parameter.

      - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    
       // Receive push notifications
       LBPushNotification* notification = [LBPushNotification application:application
                                             didReceiveRemoteNotification:userInfo];
       [self.pnListVC addPushNotification:notification];
    
      }
    

     

    Now we know how to enable the iOS application to interact with LoopBack push notification services. Let’s review the architecture behind.

    Architecture

    Key Components

    • Device model and APIs to manage devices with applications and users
    • Application model to provide push settings for device types such as ios and android
    • Notification model to capture notification messages and persist scheduled notifications
    • Optional Job to take scheduled notification requests
    • Push connector that interact with device registration records and push providers such as APNS, GCM, and MPNS.
    • Push model to provide high level APIs for device-independent push notifications

     

    What’s Next?

     

    Further Reading