Budgie IOS: Let’s walk through the journey of sending push notifications from Salesforce to an IOS device through Apple’s APN – Part 2. Connected Apps, MobilePushServiceDevice, Batch Apex and PushNotificationPayload

Posted by

Hello readers! In my previous post, we covered generating signed JWT token in Apex, attempting to use HTTP requests with Apple’s APN framework, a working curl request example of sending push notifications and the possibility of using a connected app to solve this problem.

Okay, lets get into it! What is the point of this post? Well, part of the work that I have been doing on the Budgie iOS family budgeting app is to allow us to be informed about our spending without physically going to the app. This would allow us to be alerted on the fly about high spending, how much we spent yesterday, how we are trending towards the monthly budget and any helpful insights that we could drive action from. Push notifications sent from Salesforce to the Budgie app would be a great solution for this and I wanted to learn more about how I could integrate my Salesforce backend with a custom iOS mobile app. In my previous post, we learned that http2 protocol is not supported in Salesforce and therefore we must send it another way.

Sending Notifications with Connected Apps

Due to the limitation of Salesforce supporting the http2 protocol, you can instead use the Mobile App Settings section in the connected app you create for your IOS application to access your Salesforce data.

When you enable push messaging, you can configure your connected app with the ability to send push notifications to your custom app with the following attributes:

  • Platform: The device platform that you will be sending push notifications to. (Remember that if you wish to send push notifications to both platforms, you must create two separate connected apps for both use cases)
    • Apple
    • Android
  • Environment:
    • Sandbox
    • Production
  • Application Bundle Id:
    • This is the application bundle id that is assigned to your custom IOS/Android app
  • Token Authentication Settings:
    • Signing Key: the p8 file that Apple provides you when you active the push notification framework for your app developer account
    • Key Identifier: this is also assigned to you when you activate the push notification framework
    • Team Identifier: this is the team identifier to your overall Apple developer account

Here is what it should look like after you save your push messaging settings.

Awesome! Now that we have our mobile app settings configured for our connected app, its time to test our notifications using the framework.

Connected apps allow you to test out sending your IOS push notifications directly from the connected app. Lets check out what all of these different items mean:

Recipient: Device token from Apple Push Notification service

Alert: The information for displaying an alert.

Badge: The number to display in a badge on your app’s icon. Specify 0 to remove the current badge, if any.

Sound: The name of a sound file in your app’s main bundle or in the Library/Sounds folder of your app’s container directory. Specify the string “default” to play the system sound. Use this key for regular notifications. For critical alerts, use the sound dictionary instead. For information about how to prepare sounds, see UNNotificationSound.

Content-Available: The background notification flag. To perform a silent background update, specify the value 1 and don’t include the alertbadge, or sound keys in your payload. See Pushing Background Updates to Your App.

Custom payload: Custom payload content, outside of the aps namespace. Must be in valid JSON format.

Its a pretty cool feature to be able to test your IOS notifications from your Connected App. Lets dig into the recipient field a bit. There is a lot to unpack here. As mentioned above, the recipient field is the device token from your Apple Push Notification service, but…

What does that mean, why is there a search icon (clearly indicating that its a lookup), and how do you avoid having to hard code the ID and use an actual Salesforce record?

MobilePushServiceDevice

The MobilePushServiceDevice is a Salesforce object that allows you to create Mobile Push Registration records and associate them to a given user. Ever seen the Mobile Push Registrations field on the user record with a link to View? Yeah, I hadn’t either until I explored push notifications from Salesforce.

The Mobile Push Registrations holds the following:

  • Service Type: the platform you are sending your pushing notifications through
  • App Name: my IOS app Budgie
  • Connection Token: your recipient token that you are using to test
  • Last Registration Date: the date that you agree to receive notifications with the app (we will get to that later)
  • Created Date: created date of the Mobile Push Registrations

How do we actually create these? The answer: Salesforce REST API. See the swift code below:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
    let token = tokenParts.joined()
        
    let json: [String: Any] = [
        "ConnectionToken": token,
        "ServiceType": "Apple",
        "ApplicationBundle": Bundle.main.bundleIdentifier as Any
    ]

    IntegrationUtility.callSalesforceRestAPI(
    with: "https://ap1.salesforce.com/services/data/v53.0/sobjects/MobilePushServiceDevice", 
    parameters: json,
    httpMethod: "POST",
    contentType: "application/json",
    type: SalesforceVariables.mobileUserAccessCredentialKey.accessTokenKey) {response in
        if (response?.code == 201) {
            // PROCESS EXPENSES DATA HERE
            print("--- The device was successfully registered in Salesforce for push notifications ---")
        }
    }
}

In this Swift code, we use the didRegisterForRemoteNotificationsWithDeviceToken which confirms that the user has agreed to receiving push notifications and now we can store their token in Salesforce. The MobilePushServiceDevice SObject takes a body that includes all of the information that we went over above in the Mobile Push Registrations view. Also, this is important! This object is only available from the consumer key and secret in the Connected App that has Push Messaging Enabled.

Having these records in Salesforce is the first step. We still need to write automation to support sending the push notifications, but what framework and class structure can we use to do that?

Batch Apex and PushNotificationPayload

Going back to the original use case, I wanted a daily push notification to go out to me and my wife letting us know how much we spent the day before and what our most expensive item was. A scheduled job with a batch class was the perfect tool for the job. This batch queries for all of the expense items that were created yesterday, iterates through them to find the highest expense and the total expense and builds out the string of the text that will go to us each morning.

‘Good morning! You spent a total of $’ + String.valueOf(amount) + ‘ yesterday. Your highest item was a ‘ + highestExpenseCategoryYesterday + ‘ expense of $’ + String.valueOf(highestExpenseAmountYesterday) + ‘ at ‘ + highestExpenseCompanyYesterday;

public with sharing class YesterdaysSpendingPushNotificationBatch implements Database.Batchable<sObject> {

    public final String query;

    public final String spendingType = 'Variable';

    public YesterdaysSpendingPushNotificationBatch() {
        query = 'SELECT Id, Amount__c, Company__c, Expense_Category__c FROM Expense_Item__c WHERE Date__c = YESTERDAY AND Expense_Month__r.Expense__r.Spending_Type__c = :spendingType';    
    }

    public Database.QueryLocator start(Database.BatchableContext BC){
        return Database.getQueryLocator(query);
    }

    public void execute(Database.BatchableContext BC, List<Expense_Item__c> expenseItemsYesterday){
        Messaging.PushNotification msg = new Messaging.PushNotification();

        Double amount = 0.00;
        Double highestExpenseAmountYesterday = 0.00;
        String highestExpenseCompanyYesterday = 'N/A';
        String highestExpenseCategoryYesterday = 'N/A';
        for (Expense_Item__c ei : expenseItemsYesterday) {
            amount += ei.Amount__c;
            if (ei.Amount__c > highestExpenseAmountYesterday) {
                highestExpenseAmountYesterday = ei.Amount__c;
                highestExpenseCompanyYesterday = ei.Company__c;
                highestExpenseCategoryYesterday = ei.Expense_Category__c;
            }
        }

        String alert = 'Good morning! You spent a total of $' + String.valueOf(amount) + ' yesterday. Your highest item was a ' + highestExpenseCategoryYesterday + ' expense of $' + String.valueOf(highestExpenseAmountYesterday) + ' at ' + highestExpenseCompanyYesterday;
        Map<String, Object> payload = Messaging.PushNotificationPayload.apple(alert, '', null, null);

        // Adding the assembled payload to the notification
        msg.setPayload(payload);

        // Adding recipient users to list
        Set<String> usersToSend = new Set<String>();
            
        List<GroupMember> budgieGroupMembers = [SELECT UserOrGroupId from GroupMember where Group.Name = 'Budgie'];
        for (GroupMember member : budgieGroupMembers) {
            System.debug('what is the value of member? ' + member);
            usersToSend.add(member.UserOrGroupId);
        }

        // Sending the notification to the specified app and users.
        // Here we specify the API name of the connected app.  
        msg.send('Budgie', usersToSend);
    }

    public void finish(Database.BatchableContext BC){

    }
}

After the message has been constructed, we use the Messaging.PushNotificationPayload method to setup the payload for our push notifications.

public static Map<String,Object> apple(
    String alert,
    String sound,
    Integer badgeCount,
    Map<String,Object> userData
)

After we build the payload, I created a Salesforce Group that can manage all of the mobile users that we can query for. Once we have all of our users, we can use the Messaging.PushNotification instance that we created in the beginning of the execute of the batch to send the push notifications. Let’s see what it looks like:

It was super fun to go through this exercise and now my wife and I have insight into our spending every morning which keeps us motivated to save money.

Thanks so much for reading and happy coding!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s