Push Notifications
Requirements
Configure handling Push Notifications in your application. See Apple Developer - Notifications.
Configuring Firebase
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.
Setting 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
}
} else {
let settings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
}
// MARK: - MessagingDelegate
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
Client.registerForPush(registrationToken: fcmToken, success: { (success) in
// success
}) { (error) in
// failure
}
}
Make sure that the Firebase token is always up-to-date by implementing a suitable method from the SyneriseDelegate
(see the section):
// MARK: - SyneriseDelegate
func snr_registerForPushNotificationsIsNeeded() -> Void {
guard let fcmToken = Messaging.messaging().fcmToken else {
return
}
Client.registerForPush(registrationToken: fcmToken, success: { (success) in
// success
}) { (error) in
// failure
}
}
Configuring Notification Encryption
To enable encrypted Push Notifications, you must the change configuration of your workspace in the Synerise portal. See Google Firebase.
Set your Keychain Group Identifier (See this section) and set Synerise.settings.notifications.encryption
to true
in SDK settings:
Synerise.settings.sdk.keychainGroupIdentifier = "YOUR_KEYCHAIN_GROUP_IDENTIFIER"
Synerise.settings.notifications.encryption = true
Next: Configure Synerise Notification Service Extension.
Handling incoming Push Notifications
Documentation on how to prepare Push Notifications on app.synerise.com is available in our Mobile Knowledge Base.
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))
}
}
Custom payload
You may send custom Push Notifications or all custom campaigns on app.synerise.com. The code below of one sample delegate method checks if notification is not Synerise origin and then deal with 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))
}
}
Encrypted payloads
Synerise payload
If you handle Synerise notification, you do not have to do anything. The SDK decrypts Synerise notification’s payload:
- In Notification Service Extension for Push Notifications
- In the SDK, after invoking
Synerise.handleNotification(_:)
for Silent Push Notifications
Custom payload
Otherwise, if it is custom encrypted Push Notification sended by Synerise, 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.
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 == false {
let isNotificationEncrypted: Bool = Synerise.isNotificationEncrypted(userInfo)
if isNotificationEncrypted == true {
guard let userInfoDecrypted = Synerise.decryptNotification(userInfo) else {
fatalError();
}
bestAttemptContent.title = userInfoDecrypted["aps"]?["alert"]?["title"]
bestAttemptContent.body = userInfoDecrypted["aps"]?["alert"]?["body"]
bestAttemptContent.userInfo = userInfoDecrypted;
}
}
//...
contentHandler(bestAttemptContent)
}
}
}
‘Mutable-Content’ parameter
If you want to deal with Push Notifications data before application receives it, tick Mutable-Content
option if you send the Simple Push communication.
If you want to have full support for Simple Push communication, and to make Mutable-Content
relevant and working properly, you must configure Synerise Notification Service Extension.
‘Content-Available’ parameter
If you want to receive push notification in background or resume state, tick Content-Available
option if you send the Simple Push communication.
See Apple Developer - Pushing Background Updates to Your App for more details.
Synerise Notification Service Extension
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 it is configured).
- Tracking events from Simple Push communication.
- Adding action buttons to Simple Push communication (if these are added in the communication).
- Improving the appearance of Simple Push communication(Rich Media - Single Image) with 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.
- Remember that your host application and all the Notification Content Extensions must have the same iOS Deployment Target version (newer than iOS 10).
- Remember to select the
Mutable-Content
option if you send the Simple Push campaign to enable possible processing that by Notification Service Extension.
Implementation
import UserNotifications
import SyneriseSDK
class NotificationService: UNNotificationServiceExtension, NotificationServiceExtensionDelegate {
var contentHandler: ((UNNotificationContent) -> Void)?
var receivedRequest: UNNotificationRequest?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
self.receivedRequest = request
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"
#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 request = self.receivedRequest, let bestAttemptContent = self.bestAttemptContent {
NotificationServiceExtension.serviceExtensionTimeWillExpireRequest(receivedRequest, withMutableNotificationContent:self.bestAttemptContent);
bestAttemptContent.title = "Fallback title"
bestAttemptContent.body = "Fallback body"
contentHandler(bestAttemptContent)
}
}
// MARK: - NotificationServiceExtensionDelegate
func notificationServiceExtensionDidFailProcessingWithError(error: Error) {
#if DEBUG
self.bestAttemptContent.body = error.localizedDescription;
#endif
}
func notificationServiceExtensionDidFailDecryptionWithError:(NSError *)error {
#if DEBUG
self.bestAttemptContent.body = error.localizedDescription;
#endif
}
}
See the examples Notification Service Extensions with implemented notification decryption:
Debug Mode
You can enable debug mode for Notification Service Extension logging and testing purposes.
You can debug iOS Notification Service Extension and add breakpoints. To enable this, in Xcode go to Debug > Attach to Process by PID or Name… and enter your Notification Service Extension process name.
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
//...
NotificationServiceExtension.setDebugModeEnabled(true)
//...
}
In debug mode, decryption process is always successful.
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.
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 long 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 inherited from a suitable Synerise SDK class.
Configuration
To add this feature in your Simple Push Campaigns, you must:
- Configure App Group Identifier (see this section).
- Configure Keychain Group Identifier (see this section).
- Add the Notification Content Extensions in your iOS project - separately for each type of our Rich Media extensions.
- Configure with the SDK the host application and the notification content extensions.
- 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.
- Remember that your host application and all the Notification Content Extensions must have the same iOS Deployment Target version (newer 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. 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: [])
UNUserNotificationCenter.current().setNotificationCategories([singleMediaCategory, carouselCategory])
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)
}
}
See the complete example: Single Media Notification Content Extension
Properties
Parameter | Type | Default | Description |
---|---|---|---|
contentViewIsScrollable | Bool |
true | This parameter specifies if vertical scroll is enabled. If false, content is adjusted to 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.
- configured without storyboard (it is set by default).
- correlated with the principal class.
$(PRODUCT_MODULE_NAME).
is required. For Objective-C, it is not.
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)
}
}
See the complete example: Carousel Notification Content Extension
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.
- configured without storyboard (it is set by default).
- correlated with the principal class.
$(PRODUCT_MODULE_NAME).
is required. For Objective-C, it is not.