Configuring push notifications (iOS)

Prerequisites


Set up Firebase Cloud Messaging for Synerise SDK


Extend the Firebase Messaging Delegate so our SDK can receive the Firebase token that is required to deliver push notifications from Synerise:

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    FirebaseApp.configure()
   	Messaging.messaging().delegate = self

    if #available(iOS 10, *) {
      UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
      if granted {
        DispatchQueue.main.async {
          UIApplication.shared.registerForRemoteNotifications()
        }

        guard let fcmToken = Messaging.messaging().fcmToken else {
          return
        }

        let mobilePushAgreement = true // true or false, should depend on device permissions and customer's agreement in the application

        Client.registerForPush(registrationToken:fcmToken, mobilePushAgreement:mobilePushAgreement, success: { (success) in
          // success
        }) { (error) in
          // failure
        }
      }
    } else {
      let settings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
      application.registerUserNotificationSettings(settings)
    }
  }

  // MARK: - MessagingDelegate

  func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
    if let registrationToken = fcmToken {
      let mobilePushAgreement = true // true or false, should depend on device permissions and customer's agreement in the application

      Client.registerForPush(registrationToken:registrationToken, mobilePushAgreement:mobilePushAgreement, success: { (success) in
        // success
      }) { (error) in
        // failure
      }
    }
  }

Important: The second parameter of the registration method is the agreement for mobile push campaigns. In the Profile’s card in Synerise, you can find it in the Subscriptions section (if you have the required access permission). Learn more about the Client.registerForPush(registrationToken:mobilePushAgreement:success:failure:) method in the method reference.

Keep Firebase token always up-to-date


You must always keep the Firebase token updated. In many cases in the application lifecycle, such as authorization, destroyed sessions, user context change, periodic jobs (Background Tasks), and so on, the registration needs to be updated. In these situations, the SDK invokes the snr_registerForPushNotificationsIsNeeded(origin:) method or snr_registerForPushNotificationsIsNeeded() method.

Note: Background Tasks) allows you to keep the Firebase token updated even if the host application is not launched for a long time. It launches your app in the background approximately every 20 days and refreshes the token so it stays up to date.
// MARK: - SyneriseDelegate

func snr_registerForPushNotificationsIsNeeded(origin: PushNotificationsRegistrationOrigin) -> Void {
  guard let fcmToken = Messaging.messaging().fcmToken else {
    return
  }

  let mobilePushAgreement = true // true or false, depending to customer's agreement in the application

  Client.registerForPush(registrationToken:fcmToken, mobilePushAgreement:mobilePushAgreement, success: { (success) in
    // success
  }) { (error) in
    // failure
  }
}

Configure Notification Encryption


To enable encrypted push notifications, you must change the configuration of your workspace in the Synerise portal. See Google Firebase.

Note: iOS 10 or higher version is required for this feature.

Set your Keychain Group Identifier (see this section) and enable Synerise.settings.notifications.encryption in SDK settings:

Synerise.settings.sdk.keychainGroupIdentifier = "YOUR_KEYCHAIN_GROUP_IDENTIFIER"
Synerise.settings.notifications.encryption = true

Next: Configure Synerise Notification Service Extension.

Synerise Notification Service Extension


Synerise Notification Service Extension is an object that adds the notification functionality to the SDK. It works by implementing the UNNotificationServiceExtension that cooperates with the host application.

The Synerise Notification Service Extension facilitates some operations by automating them. This means a one-time implementation provides new functionalities, changes, and fixes, along with new versions of the SDK.

It implements the following operations:

  • Decrypting Simple Push communication data (if encryption is enabled).
  • Tracking events from Simple Push communication.
  • Adding action buttons to Simple Push communication (if the communication contains any).
  • Improving the appearance of Simple Push communication (Rich Media - Single Image) with an image thumbnail.

Configuration

  1. Configure App Group Identifier (see this section).
  2. Configure Keychain Group Identifier (see this section).
  3. Add the Notification Service Extension to your iOS project (Apple Developer - UNNotificationServiceExtension).
  4. Configure the SDK both in the host application and in the notification service extension.
Important:
  • Configuring App Group Identifier and Keychain Group Identifier both in the host application and in the notification service extension is required for proper functioning of all Notification Service Extension features.
  • Your host application and the Notification Service Extension must have the same iOS Deployment Target version (newer than iOS 10).
  • If you want to enable processing the campaign by Notification Service Extension, select the Mutable-Content option.

Implementation

import UserNotifications
import SyneriseSDK

class NotificationService: UNNotificationServiceExtension {

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

  override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    self.contentHandler = contentHandler
    self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
    
    if let bestAttemptContent = self.bestAttemptContent {
        Synerise.settings.sdk.appGroupIdentifier = "YOUR_APP_GROUP_IDENTIFIER"
        Synerise.settings.sdk.keychainGroupIdentifier = "YOUR_KEYCHAIN_GROUP_IDENTIFIER"
        
        NotificationServiceExtension.setDelegate(self)
        NotificationServiceExtension.setNotificationDelegate(self)

        #if DEBUG
        NotificationServiceExtension.setDebugModeEnabled(true)
        #endif
        
        NotificationServiceExtension.setDecryptionFallbackNotificationTitleAndBody(title: "(Encrypted))", body: "(Encrypted)")
        NotificationServiceExtension.didReceiveNotificationExtensionRequest(request, withMutableNotificationContent: 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 = self.contentHandler, let bestAttemptContent =  self.bestAttemptContent {
        contentHandler(bestAttemptContent)
    }
  }
}

extension NotificationService: NotificationServiceExtensionDelegate {
  
  func notificationServiceExtensionDidFailProcessingWithError(_ error: Error) {
#if DEBUG
        self.bestAttemptContent?.title = error.localizedDescription
#endif
  }
    
  func notificationServiceExtensionDidFailDecryptionWithError(_ error: Error) {
#if DEBUG
    self.bestAttemptContent?.title = error.localizedDescription
#endif
  }
}

extension NotificationService: NotificationDelegate {
    // This method is called when a Synerise notification is received.
    func snr_notificationDidReceive(notificationInfo: NotificationInfo) {
        //...
    }
}

Examples of Notification Service Extensions:

Debug Mode

You can enable the debug mode for Notification Service Extension logging and testing purposes.

WARNING: Do not use the debug mode in a release version of your application.
1
2
3
4
5
6
7
8
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
  self.contentHandler = contentHandler
  self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

  //...
  NotificationServiceExtension.setDebugModeEnabled(true)
  //...
}
WARNING: In the debug mode, decryption process is considered successful even if it fails. Your best attempt content (UNNotificationContent object) is modified - the notification displays the title and body with the problem that occurred during decryption. It may help you debug and find problems with the configuration.

Handling incoming push notifications


Note: You may disable handling push notifications in the SDK at any time. See Enable/disable notifications.

Documentation on how to prepare push notifications in app.synerise.com is available in our user guide.

In order to handle Synerise push notifications, you must pass the incoming push payload to the Synerise SDK.

Synerise payload

The following code shows how to handle push notifications in the AppDelegate:

// Support for Push Notifications on iOS 9
// Support for Silent Notifications

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  let isSyneriseNotification: Bool = Synerise.isSyneriseNotification(userInfo)
  
  if isSyneriseNotification {
    Synerise.handleNotification(userInfo)
    completionHandler(.noData)
  }
}

func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
  let isSyneriseNotification: Bool = Synerise.isSyneriseNotification(userInfo)
  
  if isSyneriseNotification {
    Synerise.handleNotification(userInfo, actionIdentifier: identifier)
    completionHandler()
  }
}

// Support for Push Notifications on iOS 10 and above

// MARK: - UNUserNotificationCenterDelegate

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
  let userInfo = response.notification.request.content.userInfo
  
  let isSyneriseNotification: Bool = Synerise.isSyneriseNotification(userInfo)
  
  if isSyneriseNotification {
    Synerise.handleNotification(userInfo, actionIdentifier: response.actionIdentifier)
    completionHandler()
  }
}

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
  let userInfo = notification.request.content.userInfo

  let isSyneriseNotification: Bool = Synerise.isSyneriseNotification(userInfo)
  
  if isSyneriseNotification {
    Synerise.handleNotification(userInfo)
    completionHandler(UNNotificationPresentationOptions.init(rawValue: 0))
  }
}
Important: All of these methods must be implemented to ensure proper handling of push notifications.
Important: Displaying the notification banner on top of the screen in foreground state depends on values passed in completionHandler in the UNUserNotificationCenterDelegate.userNotificationCenter(_:willPresent:completionHandler:) method.

Custom payload

You may send both custom push notifications and custom campaigns in Synerise. The code below of one sample delegate method checks if the notification origin and then handles it.

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
  let userInfo = notification.request.content.userInfo

  let isSyneriseNotification: Bool = Synerise.isSyneriseNotification(userInfo)

  if isSyneriseNotification {
    Synerise.handleNotification(userInfo)
    completionHandler(UNNotificationPresentationOptions.init(rawValue: 0))
  } else {
    // Handle other notification in your own way
  }
}

Encrypted payloads

If you handle the Synerise push notification, you do not have to do anything. The SDK decrypts Synerise push notification’s payload:

Otherwise, if it is a custom encrypted push notification sent by Synerise, or you need decrypt data from the push notification, there are two methods for dealing with them:

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
  let userInfo = notification.request.content.userInfo

  let isSyneriseNotification: Bool = Synerise.isSyneriseNotification(userInfo)

  if isSyneriseNotification {
    let isNotificationEncrypted: Bool = Synerise.isNotificationEncrypted(userInfo)
    if isNotificationEncrypted == true {
        if let userInfoDecrypted = Synerise.decryptNotification(userInfo) {
            // Handle decrypted payload in your own way
        }

    Synerise.handleNotification(userInfo)
    completionHandler(UNNotificationPresentationOptions.init(rawValue: 0))
  }
}

Remember, if you want to send custom push notifications by Synerise (and it is not a silent push notification), you must implement the code in your Notification Service Extension:

import UserNotifications
import SyneriseSDK

class NotificationService: UNNotificationServiceExtension {

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

  override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    self.contentHandler = contentHandler
    self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
    
    if let bestAttemptContent = self.bestAttemptContent {
      var userInfo = notification.request.content.userInfo
      let isSyneriseNotification: Bool = Synerise.isSyneriseNotification(userInfo)

      if isSyneriseNotification == true {
        //...
      } else {
        let isNotificationEncrypted: Bool = Synerise.isNotificationEncrypted(userInfo)
        if isNotificationEncrypted == true {
          if let userInfoDecrypted = Synerise.decryptNotification(userInfo) {
            bestAttemptContent.title = userInfoDecrypted["aps"]?["alert"]?["title"]
            bestAttemptContent.body = userInfoDecrypted["aps"]?["alert"]?["body"]
            bestAttemptContent.userInfo = userInfoDecrypted;
          } else {
            bestAttemptContent.title = "YOUR_FALLBACK_TITLE"
            bestAttemptContent.body = "YOUR_FALLBACK_BODY"
          }
        }
      }

      contentHandler(bestAttemptContent)
    }
  }
}

‘Content-Available’ parameter

If you want to receive push notification in the background and foreground states, enable the Content-Available option while creating a push notification in Synerise (Creating mobile push templates).

`Content-Available` option in visual builder
Enabled `Content-Available` option in a visual builder

When you want support this option, you must add the capability to your application. In the Signing and Capability tab, in the Background Modes capability, select the Remote notifications checkbox:

Remote notifications capability in the Xcode
Remote notifications capability in the Xcode

Your application will be notified of the notification delivery when it’s in the foreground or background (the app will be woken up). This ensures that the necessary method and code responsible for receiving background notifications are executed. On iOS, it calls your app delegate’s application(_:didReceiveRemoteNotification:fetchCompletionHandler:) method. On watchOS, it calls your extension delegate’s didReceiveRemoteNotification(_:fetchCompletionHandler:) method.

For more details, see Apple Developer - Pushing Background Updates to Your App.

‘Mutable-Content’ parameter

If you want your notification to be processed by the Notification Service Extension, enable the Mutable-Content option while creating a push notification in Synerise (Creating mobile push templates).

`Mutable-Content` option in visual builder
Enabled `Mutable-Content` option in a visual builder

If you want to have full support for Simple Push communication, and to make Mutable-Content relevant and functional, you must configure Synerise Notification Service Extension. This extension is required to fully support Simple Push communication for iOS, such as gathering the view events.

For more details, see Apple Developer - Modifying content in newly delivered notifications.

Delegate methods

Note: NotificationDelegate is available from SDK version 4.10.0.

A NotificationDelegate handles events from Synerise notifications.

  • To handle “receive” events when an application is disabled or in background state: set the delegate in the notification service extension by using the NotificationServiceExtension.setNotificationDelegate(_:) method. See this section to get a sample code for the notification service extension.
  • SDK version 4.14.3 or newer: To handle “receive” events when an application is launched by the notification or in foreground state: set the delegate in the application by using the Synerise.setNotificationDelegate(_:) method.
  • To handle “dismiss” and “click” events: set the delegate in the application by using the Synerise.setNotificationDelegate(_:) method.

See a sample code from the application below:

extension SyneriseManager: NotificationDelegate {
    // This method is called when a Synerise notification is received.
    func snr_notificationDidReceive(notificationInfo: NotificationInfo) {
        //...
    }

    // This method is called when a Synerise notification is dismissed.
    func snr_notificationDidDissmis(notificationInfo: NotificationInfo) {
        //...
    }
    
    // This method is called when a Synerise notification is clicked.
    func snr_notificationClicked(notificationInfo: NotificationInfo) {
        //...
    }
    
    // This method is called when an action button is clicked in a Synerise notification.
    func snr_notificationClicked(notificationInfo: NotificationInfo, actionButton: String) {
        //...
    }
}

Handling actions from push notifications


Additional in-app alert from push notifications


The iOS SDK can display an additional alert in the application after a push notification is received. See this article to read more about this feature.

Simple Push campaign with in-app alert
Simple Push campaign with in-app alert

Rich Media in push notifications


Notification Content Extension is an object that allows rendering your own appearance of a push notification when the notification is expanded (by tapping the notification). It works by implementing the UNNotificationContentExtension that cooperates with the host application.

Synerise SDK does most of the work needed and provides classes for the Notification Content Extensions. When you create an extension, you only need to make it inherit from a suitable Synerise SDK class.

Prerequisites

  1. Configure App Group Identifier (see this section).
  2. Configure Keychain Group Identifier (see this section).

Configuration

To add this feature in your Simple Push Campaigns, you must: 3. Add the Notification Content Extensions in your iOS project - separately for each type of our Rich Media extensions. 4. Configure the host application and the notification content extensions with the SDK.

Important:
  • Configuring App Group Identifier and Keychain Group Identifier both in the host application and in the notification service extension is required for proper functioning of all Notification Content Extensions features.
  • Your host application and all the Notification Content Extensions must have the same iOS Deployment Target version (higher than iOS 10).

Application implementation

You must create push notification categories with the right identifiers, correlating with Content Extensions that you added before. The identifiers must be taken from the Synerise SDK constants (they are used in the code sample below). This does not affect button names.

Synerise.settings.sdk.appGroupIdentifier = "YOUR_APP_GROUP_IDENTIFIER"
Synerise.settings.sdk.keychainGroupIdentifier = "YOUR_KEYCHAIN_GROUP_IDENTIFIER"

let singleMediaCategory = UNNotificationCategory(identifier: SNRSingleMediaContentExtensionViewControllerCategoryIdentifier, actions: [], intentIdentifiers: [], options: [])

let carouselPrevious = UNNotificationAction(identifier: SNRCarouselContentExtensionViewControllerPreviousItemIdentifier, title: "Previous", options: [])
let carouselAction = UNNotificationAction(identifier: SNRCarouselContentExtensionViewControllerChooseItemIdentifier, title: "Go!", options: UNNotificationActionOptions.foreground)
let carouselNext = UNNotificationAction(identifier: SNRCarouselContentExtensionViewControllerNextItemIdentifier, title: "Next", options: [])
let carouselCategory = UNNotificationCategory(identifier: SNRCarouselContentExtensionViewControllerCategoryIdentifier, actions: [carouselPrevious, carouselAction, carouselNext], intentIdentifiers: [], options: [])

// Use this method when you use SDK 4.21.0 or higher
Synerise.setNotificationCategories([singleMediaCategory, carouselCategory])
// or
// Use this method when you use SDK lower than 4.21.0
UNUserNotificationCenter.current().setNotificationCategories([singleMediaCategory, carouselCategory])
Method Description
Synerise.setNotificationCategories(_:) Sets the notification categories (including Synerise categories) that your app supports. Use this method when you use SDK 4.21.0 or higher.
UNUserNotificationCenter.current().setNotificationCategories(_:) Sets the notification categories by using the standard iOS SDK method. Use this method when you use SDK lower than 4.21.0.

Single Media implementation

import UIKit
import UserNotifications
import UserNotificationsUI
import SyneriseSDK

class NotificationViewController: SingleMediaContentExtensionViewController, UNNotificationContentExtension {

  func didReceive(_ notification: UNNotification) {
  Synerise.settings.sdk.appGroupIdentifier = "YOUR_APP_GROUP_IDENTIFIER"
  Synerise.settings.sdk.keychainGroupIdentifier = "YOUR_KEYCHAIN_GROUP_IDENTIFIER"

    self.contentViewIsScrollable = false
    self.imageContentMode = .scaleAspectFit

    setSyneriseNotification(notification)
  }

  func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
    setSyneriseNotificationResponse(response, completionHandler: completion)
  }
}

Properties

Parameter Type Default Description
contentViewIsScrollable Bool true This parameter specifies if vertical scroll is enabled. If false, content is adjusted to the screen height.
imageContentMode UIViewContentMode UIViewContentModeScaleAspectFill This parameter sets the rendering mode of an image

Info.plist

The configuration for your Content Extension in \*.plist file must be:

  • correlated with Synerise SDK constants for notification category
    NSExtension->NSExtensionAttributes->UNNotificationExtensionCategory->0 must be synerise.notifications.category.single-media.

  • configured without a storyboard (by, default a storyboard is set).
    NSExtensionMainStoryboard key and its values must be removed from \*.plist file.

  • correlated with the principal class
    NSextensionPrincipalClass key must have value with the name of the main class for the notification extension you have created. For Content Extension written in Swift, the prefix $(PRODUCT_MODULE_NAME). is required. For Objective-C, it is not.

Example \*.plist file for single media:

Sample *.plist configuration in Single Image Notification Content Extension
Sample *.plist configuration in Single Image Notification Content Extension

Example

Examples of Single Media Notification Content Extension:

import UIKit
import UserNotifications
import UserNotificationsUI
import SyneriseSDK

class NotificationViewController: CarouselContentExtensionViewController, UNNotificationContentExtension {

  func didReceive(_ notification: UNNotification) {
  Synerise.settings.sdk.appGroupIdentifier = "YOUR_APP_GROUP_IDENTIFIER"
  Synerise.settings.sdk.keychainGroupIdentifier = "YOUR_KEYCHAIN_GROUP_IDENTIFIER"

    self.imageContentMode = .scaleAspectFit

    setSyneriseNotification(notification)
  }

  func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) {
    setSyneriseNotificationResponse(response, completionHandler: completion)
  }
}
Parameter Type Default Description
imageContentMode UIViewContentMode UIViewContentModeScaleAspectFill This parameter sets the rendering mode of images

The configuration for your Content Extension in \*.plist file must be:

  • correlated with Synerise SDK constants for notification category.
    NSExtension->NSExtensionAttributes->UNNotificationExtensionCategory->0 must be synerise.notifications.category.carousel.

  • configured without storyboard (it is set by default).
    NSExtensionMainStoryboard key and its values must be removed from \*.plist file.

  • correlated with the principal class.
    NSextensionPrincipalClass key must have value with name of your main class for the notification extension you have created. For Content Extension written in Swift, the prefix $(PRODUCT_MODULE_NAME). is required. For Objective-C, it is not.

See proper \*.plist file example for single media below:

Sample *.plist configuration in Carousel Notification Content Extension
Sample *.plist configuration in Carousel Notification Content Extension

Examples of Carousel Notification Content Extension:

😕

We are sorry to hear that

Thank you for helping improve out documentation. If you need help or have any questions, please consider contacting support.

😉

Awesome!

Thank you for helping improve out documentation. If you need help or have any questions, please consider contacting support.

Close modal icon Placeholder alt for modal to satisfy link checker