MoEngage Docs

Push Notification Implementation

PREREQUISITE :

Make sure you have created APNS certificate and uploaded it to MoEngage dashboard as mentioned in APNS Certificate/ PEM file before testing push notification.

App Target Implementation

Settings Changes:

Capabilities Tab Changes

First, select your App Target and select Capabilities do the changes as shown in the image below:

  1. Turn ON App Groups in for your app target and enable one of the App group ids, in case if you don't have an App Group ID then create one. The name of your app group should be group.{your_bundle_id}.MoEngage.

  2. Turn ON Background mode and set/enable Remote Notification.

  3. Turn ON the Push Notifications capability for your app.

NOTE:

On enabling Remote Notification background mode, we will be able to track uninstalls even for devices where push notification is disabled by the user.

Adding UserNotifications framework

In the App's Target add UserNotifications framework in Linked Frameworks and Libraries and set it Optional.

Code Changes in App Target:

Provide the App Group ID to SDK

Provide the App Group ID selected in Capabilities tab by calling setAppGroupID: method before initializing the SDK as shown below:

MoEngage.setAppGroupID(<Your AppGroupID>)
[MoEngage setAppGroupID:<Your AppGroupID>];

AppDelegate swizzling in SDK

Segment-MoEngage Integration

Please note that AppDelegate Swizzling doesn't work reliably with Segment Integration because of delay in initializing the SDK by Segment, therefore make sure to call the MoEngage SDK methods for all Push related callbacks.

In MoEngage SDK we have now swizzled the AppDelegate Class to get all the callbacks related to Push Notifications and also we have applied method swizzling for UserNotificationCenter delegate methods. This to ease the integration of the SDK, and this is introduced from the SDK version 5.0.0.

In case you prefer not to use swizzling, you can disable the same by adding the flag MoEngageAppDelegateProxyEnabled in the app’s Info.plist file and setting it to bool value NO. In the following sections, we have provided the SDK methods to be called when you get callbacks related to push notifications. Most of them will not be needed in case swizzling is enabled, the same will be mentioned in the description.

Registering for Push notification

Make sure that class, where UserNotificationCenter delegate methods are implemented, should agree to UNUserNotificationCenterDelegate , also set the UNUserNotificationCenterDelegate after the app launch in application:DidFinishLaunchingWithOptions: as shown below: (In this case AppDelegate is set to be UserNotificationCenter delegate) :

Call SDK's registerForRemoteNotificationWithCategories: to initiate registration of remote notifications as shown below, also in case if you are supporting iOS version below iOS10 we have a separate method for below versions(registerForRemoteNotificationForBelowiOS10WithCategories:) :

if #available(iOS 10.0, *) {
	MoEngage.sharedInstance().registerForRemoteNotification(withCategories: nil, withUserNotificationCenterDelegate: self)
} else {  
	MoEngage.sharedInstance().registerForRemoteNotificationForBelowiOS10(withCategories: nil)
}
if (@available(iOS 10.0, *)) {
        [[MoEngage sharedInstance] registerForRemoteNotificationWithCategories:nil withUserNotificationCenterDelegate:self];
} else {
        [[MoEngage sharedInstance] registerForRemoteNotificationForBelowiOS10WithCategories:nil];
}

Now after registering for push, the below-given callback methods will be called. In case you have disabled swizzling, call the respective MoEngage SDK methods for the callbacks as shown below :

//Remote notification Registration callback methods
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  //Call only if MoEngageAppDelegateProxyEnabled is NO
  MoEngage.sharedInstance().setPushToken(forPush: deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
  //Call only if MoEngageAppDelegateProxyEnabled is NO
	MoEngage.sharedInstance().didFailToRegisterForPush()
}
    
func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
  //Call only if MoEngageAppDelegateProxyEnabled is NO
	MoEngage.sharedInstance().didRegister(for: notificationSettings)
}
//Remote notification Registration callback methods
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
  //Call only if MoEngageAppDelegateProxyEnabled is NO
	[[MoEngage sharedInstance] setPushToken:deviceToken];
}

-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  //Call only if MoEngageAppDelegateProxyEnabled is NO
	[[MoEngage sharedInstance]didFailToRegisterForPush];
}

//This method is for getting the types of notifications that app may use 
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{
  //Call only if MoEngageAppDelegateProxyEnabled is NO
	[[MoEngage sharedInstance]didRegisterForUserNotificationSettings:notificationSettings];
}

Notification Actions

You can send the set of categories(UNNotificationCategory for iOS10 and MONotificationCategory for earlier versions of iOS) for supporting Notification actions. Get more info regarding notification actions here.

Callback methods on receiving Push Notification:

Below are the callbacks the app would receive on receiving the push notifications. In case you have disabled swizzling, include calls to MoEngage SDK methods on receiving notification callbacks as shown below:

// MARK:- UserNotifications Framework callback method
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
    
    //Call only if MoEngageAppDelegateProxyEnabled is NO
    MoEngage.sharedInstance().userNotificationCenter(center, didReceive: response)
    
    //Custom Handling of notification if Any
    let pushDictionary = response.notification.request.content.userInfo
    print(pushDictionary)
    
    completionHandler();
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    
    //This is to only to display Alert and enable notification sound
    completionHandler([.sound,.alert])
    
}
// MARK:- Remote notification received callback method for iOS versions below iOS10
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
    
    //Call only if MoEngageAppDelegateProxyEnabled is NO
    MoEngage.sharedInstance().didReceieveNotificationinApplication(application, withInfo: userInfo,
openDeeplinkUrlAutomatically: true)
  
}
// UserNotifications Framework Callback for iOS10 and above
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)())completionHandler{
    
           //Call only if MoEngageAppDelegateProxyEnabled is NO       
           [[MoEngage sharedInstance] userNotificationCenter:center didReceiveNotificationResponse:response];
           
           //Custom Handling of notification if Any  
           completionHandler();
}


- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
           
           //This is to only to display Alert and enable notification sound
           completionHandler((UNNotificationPresentationOptionSound
                       | UNNotificationPresentationOptionAlert ));
}

//Remote notification received callback method for iOS versions below iOS10
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
  			
  //Call only if MoEngageAppDelegateProxyEnabled is NO
  [[MoEngage sharedInstance]didReceieveNotificationinApplication:application withInfo:userInfo openDeeplinkUrlAutomatically:YES];
  
}

Method userNotificationCenter:willpresentnotification:withCompletionHandler: is called when the app receives notification in foreground. Here, in the completion handler you can mention how you want to let the user know that the app has received a notification.

Method userNotificationCenter:didreceivenotificationresponse:withCompletionHandler is called when the app receives a response from the user. Response can be Default Click on the Notification or Dismissing the notification or any of the other custom actions implemented using UNUserNotificationCategory. Here, call userNotificationCenter:didreceivenotificationresponse: of MoEngage class.

NOTE:

  1. userNotificationCenter:didreceivenotificationresponse:withCompletionHandler is the only method called when the user clicks on notification, if implemented. Therefore, include your custom handlers here instead of application:didreceiveremotenotification: for iOS10.

  2. While implementing deep links, make sure that you have added the apps URL Scheme to LSApplicationQueriesSchemes array in Info.plist to whitelist your app. Without this, the deep links won't work post iOS9.

Disable Badge Reset

By default, the SDK sets the notification badge count to 0 on every app launch and this also clears the notifications in the device notification center. In case if you would like to keep the notifications even after the App Launch then disable badge reset by setting disableBadgeReset property of MoEngage instance to true as shown below:

MoEngage.sharedInstance().disableBadgeReset = true
[MoEngage sharedInstance].disableBadgeReset = true;

Custom Sound for Notification

You can have a custom tone for notifications of your app. iOS platform supports .aiff , .caf & .wav files for custom Notification tone. For this make sure the sound file of tone is included in your app bundle. Once this is done make sure to provide the sound filename for Notification Sound(In Rich Content Section) while creating the campaign in the dashboard as shown below, and it should work:

Notification Service Extension Target Implementation

Why add a Notification Service Extension to your project?

Notifications have got a complete revamp after the release of iOS10 with the introduction of new UserNotifications and UserNotificationsUI framework. And with this we got Notification Service App Extensions, which can be used for following:

  1. Add media support in Notifications: Post iOS10 Apple has given us the ability to add images, gifs, audio, and video files to the notifications and this can be done using the Notification Service Extension.

  2. For supporting Inbox Feature: Notification Service Extension is also used to save the received notifications which can later be shown in the App Inbox.

  3. For Updating the Notification Badge count: MoEngage makes use of the extension to update the notification badge count and doesn't send badge in the notification payload.

  4. For Tracking Notification Impression: We can track if a Notification is received by the device using the Notification Service Extension.

Follow the below steps to set up Notification Service Extension:

1. Create a Notification Service Extension Target:

Set the name of the extension target and the programing language which you want to use:

Once the target is created, Activate the scheme for Extension when prompted for the same. After this, your extension will be added to project you will see a class with the extension name provided by you while creation and .plist file associated with it.

2. Enable Push Notification Capabilities

Then make sure that the Push Notifications Capability is enabled for the Notification Service Extension created:

3. Add UserNotifications framework to extension target:

Add UserNotifications framework to Linked Frameworks and Libraries of notification service extension target as shown below:

4. Integrate MORichNotification framework to Extension:

IMPORTANT

  • Incase, if your app is supporting iOS versions below iOS 10, there is an issue with CocoaPods wherein the MORichNotification framework is linked to the main app target, though its added only for NotificationService Extension, this results in a crash in iOS9. This is an issue on CocoaPods configuration side. To prevent this, you will have to add the MORichNotification manually from this link

Through CocoaPods :

For integrating through CocoaPod, include MORichNotification pod for your Notification Service Extension as shown below, and run pod update / install command :

target "NotificationServices" do
	pod 'MORichNotification'
end

Manual Integration :

For manual integration follow the following steps :

  1. Download the latest sdk from the following link

  2. Add the MORichNotification folder to your Notification Service Extension target.

  3. Add the following compiler flag: -ObjC. Select your Notification Service Extension target. Go to “Build Settings” ->”Linker” ->”Other Linker Flags” and add this flag.

  4. Add MORichNotification to embedded binaries in the App target, make sure to not link it with App target, since it's only being used in the Notification Extension. Refer to image below:

  1. On adding MORichNotification manually make sure to add the script for removing unsupported architectures while exporting the build OR submitting an app to the app store in the app's target. (Add it if not already done and the script is provided in 4th Step of this link)

5. Set the App Group ID for Extension:

Turn ON App Groups in for your notification service extension target and enable the same App group id which was selected for the App Target(In the above steps).

6. Code Changes in Notification Service Extension:

import UserNotifications
// 1st Step
import MORichNotification

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    
    	// 2nd Step	
      MORichNotification.setAppGroupID(<Your AppGroupID>)
    
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
        
        // To Increase badge count
        MORichNotification.updateBadge(with: bestAttemptContent)
        
        // 3rd Step
        MORichNotification.getAttachmentFrom(request) { (attachment) in
            if let att = attachment {
                self.bestAttemptContent?.attachments = [att]
            }
            
            if let bestAttemptContent = self.bestAttemptContent {
                contentHandler(bestAttemptContent)
            }
        }
        
    }
    
    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

}
#import "NotificationService.h"
// 1st Step
#import <MORichNotification/MORichNotification.h>


@interface NotificationService ()

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    @try {
      
        // 2nd Step
      	[MORichNotification setAppGroupID:<Your AppGroupID>];
      
        self.contentHandler = contentHandler;
        self.bestAttemptContent = [request.content mutableCopy];
				
      	// 3rd Step
        [MORichNotification handleRichNotificationRequest:request withContentHandler:contentHandler];
    } @catch (NSException *exception) {
        NSLog(@"MoEngage : exception : %@",exception);
    }
}


/// Save the image to disk

- (void)serviceExtensionTimeWillExpire {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
    self.contentHandler(self.bestAttemptContent);
}



@end

Refer to the code above and do the following changes:

  1. Import MORichNotification framework.
  2. Set the App Group ID selected in the settings earlier using setAppGroupID: method.
  3. Call method to process the notification request:
    • Obj-C: Call handleRichNotificationRequest: withContentHandler: method.
    • Swift: Call getAttachmentFromRequest() method and in the completion block add attachment instance and call contentHandler() as shown above.
  4. Only for Swift: Call separate method updateBadge()<Required only in case of Swift> if you intend to increase the badge count on receiving the Notification.

Rich Notification Media Limitations:

  • Refer to the following link to know about the size and format limitation for attachments(media) supported in Rich Notifications.

  • Http URL's aren't supported in iOS10 unless explicitly specified in the plist. You will have include App Transport Security Settings Dictionary in your Notification Service Extension's Info.plist and inside this set Allow Arbitrary Loads to YES.

IMAGE GUIDELINES:

  • File Size: The maximum file size for image attachments can be 10MB.
  • Dimensions: The maximum possible dimensions are 1038 x 1038 pixels. It can be anything smaller than 1038 pixels.
  • Landscape vs Portrait: iOS supports both the orientations but we recommend using images that have a landscape orientation this is because depending on the dimensions, portrait images may look too tall.

Test/Live Builds

  • If you are testing the app on Test Flight or on a live app store build, make sure you upload the adhoc or production pem to our dashboard. And also in this case you have to send push notifications from Live environment of your account.

  • For dev build, you can upload development or production certificate in dashboard, but make sure that you create your campaign in Test environment, as you cannot send push notifications to dev build from Live environment.

Notification Payload

Below is an example of the push payload sent to the app:

{
  "aps": {
    "alert": {
      "title": "Notification Title",
      "subtitle": "Notification Subtitle",
      "body": "Notification Body"
    },
    "badge": 1,
    "sound": "default",
    "category": "INVITE_CATEGORY",
    "content-available": 1,
    "mutable-content": 1
  },
  "app_extra": {
    "moe_deeplink": "moeapp://screen/settings",
    "screenName": "Screen Name",
    "screenData": {
      "key1": "val1",
      "key2": "val2"
    }
  },
  "moengage": {
    "cid": "55f2ba15a4ab4104a287bf88",
    "app_id": "DAO6UGZ73D9RTK8B5W96TPYN_DEBUG",
    "moe_campaign_id": "55f2ba15a4ab4104a287bf88",
    "moe_campaign_name": "Campaign Name",
    "inbox_expiry": "1571905058",
    "webUrl": "https://google.com",
    "couponCode": "APP200",
    "media-attachment": "https://image.moengage.com/testImg.png",
    "media-type": "image"
  }
}


Description of different keys in the payload:

  • aps: This key is used by the iOS to display the notification, and the following are the keys present within it:
    • alert : Message Content.
      • title : Gives Notification title.
      • subtitle : Gives Notification subtitle.
      • body : Gives the message body of the notification
    • badge: Gives the badge number to be displayed on top of the App Icon. MoEngage platform supports only two possible values i.e, 0/1. If the value is 1 then the SDK will increment the badge number on the app icon and if it's 0 then the badge number will be reset and there will be no badge displayed on the app icon.
    • sound: This key gives the filename of the audio file to be played on receiving the notification. If no filename is provided while creating the campaign, to play the os default sound this key is set to the value "default".
    • category: This key is used by OS for deciding the set of action buttons to be displayed for the notification. Also, the same category is used by OS to decide which Notification Content Extension target to display if present.
    • content-available: If the value of this key is set to 1, then if the app is present in the background it will get a callback(application:didReceiveRemoteNotification:fetchCompletionHandle) to refresh the app content in background. Use this key only if you have to process the push notification in background. By default, this key will be unset.
    • mutable-content: This key is by default set to 1 for all the campaigns, this is to make sure that the Notification Service Extension target gets the callback on receiving the notification to be processed by MORichNotification. If set to 0 the extension target won't get the callback.
  • app_extra: This key will contain the keys which are to be used by App Developers, i.e, Custom key value pairs and screenName for navigation.
    • moe_deeplink: This key contains the deeplinking URL if provided during the campaign creation. The SDK will process this key and will attempt to open the deeplink URL if it's valid.
    • screenName: This key gives screen name where the user has to be navigated on clicking the notification. This navigation is not done by the SDK. The possible values for this parameter are something which app developers will have to define in their project. If provided while creating the campaign, it will be present in the notification payload. And implementing the part to parse and get screenName parameter's value and to navigate to the mentioned screen has to be implemented by the app developers.
    • screenData: This contains the custom key-value pairs entered while creating the campaign, which can be made use by the app developers for any of their use-cases.
  • moengage : This will contain keys which are to be used by SDK, app developers should not be making any change to this part of the payload and also avoid using this part of the payload, as we may update the structure of this part of payload as per our need. (with the exception being cid, media-attachment, media-type, app_id which we will not change)
    • cid: Unique ID for the campaign.
    • app_id: The App ID of the account where the campaign was created.
    • moe_campain_id and moe_campaign_name : Used by analytics module to track attributes for Notification related events.
    • inbox_expiry: This key gives the timestamp at which the notification will be deleted from the app inbox.
    • webUrl: This key contains the Rich-landing URL if provided during the campaign creation. The SDK will process this key and will open the URL(if valid) in an instance of SFSafariViewController. Use Rich-landing action if you wish to open a web page inside the app on click of the push notification. For e.g. webUrl - https://www.google.com.
    • couponCode: This key contains the coupon code if provided during the campaign creation. On clicking the notification, if this key is present in the push payload the SDK will display an alert with the coupon code and will give an option to user to copy the coupon to the os clipboard.For e.g. couponCode - APP200.
    • media-attachment: The media-attachment key in the payload gives you the URL of the media which you can download.
    • media-type: Type of media present in the URL given in media-attachment i.e, image/audio/video.

Define Valid URL Schemes for DeepLinks (LSApplicationQueriesSchemes)

LSApplicationQueriesSchemes(Array - iOS) Specifies the URL schemes you want the app to be able to use with the canOpenURL: method of the UIApplication class(which is being used in our SDK). For each URL scheme you want your app to use with the deeplinks, add it as a string in this array in Info.plist. For more info follow this link.

HTTP URLs

Http URL's aren't supported in iOS9 unless explicitly specified in the plist. You will have include App Transport Security Settings Dictionary in your Info.plist and inside this set Allow Arbitrary Loads to YES.

Push Notification Implementation


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.