Configuring push notifications (iOS)
Prerequisites
-
Configure handling push notifications in your application. See Apple Developer - Notifications.
-
Google Firebase Cloud Messaging is necessary to handle Mobile Campaigns sent from Synerise.
- Follow the instructions in Firebase - Get Started on iOS.
- Integrate the Firebase with Synerise. See Integration section.
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
}
}
}
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.
// 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.
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
- Configure App Group Identifier (see this section).
- Configure Keychain Group Identifier (see this section).
- Add the Notification Service Extension to your iOS project (Apple Developer - UNNotificationServiceExtension).
- Configure the SDK both in the host application and in the notification service extension.
- 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.
|
|
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
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))
}
}
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:
- In Notification Service Extension for push notifications
- In the SDK, after invoking
Synerise.handleNotification(_:)
for silent push notifications
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:
Synerise.isNotificationEncrypted(_:)
- checks if the notification payload is encrypted by Synerise.Synerise.decryptNotification(_:)
- decrypts a notification payload.
@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).
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:
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).
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
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
- Read more about types of actions in campaigns
- Read more about 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.
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
- Configure App Group Identifier (see this section).
- 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.
- 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:
Example
Examples of Single Media Notification Content Extension:
- Single Media Notification Content Extension in Swift
- Single Media Notification Content Extension in Objective-C
Carousel implementation
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)
}
}
Properties
Parameter | Type | Default | Description |
---|---|---|---|
imageContentMode | UIViewContentMode |
UIViewContentModeScaleAspectFill |
This parameter sets the rendering mode of images |
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.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:
Example
Examples of Carousel Notification Content Extension: