Skip to main content

Push Notifications

The SDK counts push notification clicks and passes each notification to your code, so you can open the right screen. How push notifications work across platforms: Push Notifications.


Requirements​

  • The SDK installed and initialized, see Installation.
  • The FCM token passed to the SDK, see Firebase and FCM Token.
  • The Push Notifications capability in your app target.
  • Notification permission granted by the user.
  • A physical device for testing.

Setup​

Choose one of two setups. Use the first one if your app already handles notifications or uses Firebase for its own messages.

  1. Request notification permission and register with APNs in your own code, as described by Apple.
  2. Set your class as UNUserNotificationCenter.current().delegate.
  3. Pass taps to handleNotificationClick(_:) and received notifications to handleNotification(_:):
// A tap on a notification: the SDK counts the click
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
UserSDK.default?.handleNotificationClick(response.notification.request.content.userInfo)
completionHandler()
}

// A received notification (silent pushes carry in-app messages): never a click
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
UserSDK.default?.handleNotification(userInfo)
completionHandler(.noData)
}

You decide how notifications are presented in the foreground in your own userNotificationCenter(_:willPresent:withCompletionHandler:). You can pass the notification to handleNotification(_:) there as well: receiving a notification is never counted as a click.

Changed in 1.3.0

Received notifications and taps are passed separately. Before 1.3.0 every handleNotification(userInfo:) call counted a click, also for a notification that was only received, for example from willPresent. Call handleNotificationClick(_:) in userNotificationCenter(_:didReceive:withCompletionHandler:) and handleNotification(_:) in application(_:didReceiveRemoteNotification:fetchCompletionHandler:). Until you switch, the deprecated handleNotification(userInfo:) counts a click for every call, as before, so do not call it from willPresent.

Option B: the SDK handles permission and delegate​

If your app has no notification code, call registerForRemoteNotifications(options:notificationDelegate:). The SDK asks for permission if the user has not answered yet, registers with APNs and becomes the UNUserNotificationCenter delegate. It counts taps itself and shows notifications received while the app is in the foreground.

UserSDK.default?.registerForRemoteNotifications(options: [.alert, .badge, .sound], notificationDelegate: self)

You still need application(_:didReceiveRemoteNotification:fetchCompletionHandler:) with handleNotification(_:) from Option A for in-app messages.

warning

Option B replaces any UNUserNotificationCenter delegate your app or another library has set. While the app is in the foreground, the SDK shows every notification, from Positive User and others, as a banner with sound and badge.

Changed in 1.3.0

With Option B, notifications received in the foreground are shown as banners. Before 1.3.0 they only played their sound, without a banner.

Check

Send a push notification to your test contact and tap it. The click is counted in the campaign statistics, and your RemoteNotificationDelegate receives the notification.


Handling​

React to notification taps​

When the user taps a Positive User push notification, handleNotificationClick(_:) reports the click and calls your RemoteNotificationDelegate with the notification ID and the full payload. Other tapped notifications are passed to the delegate with an empty ID. handleNotification(_:) never calls the delegate.

extension AppDelegate: RemoteNotificationDelegate {
func didReceiveRemoteNotification(with id: String, data: [AnyHashable: Any]) {
guard !id.isEmpty else { return } // not a Positive User notification
if let link = data["link"] as? String, !link.isEmpty, link != "None",
let url = URL(string: link) {
// open the matching screen
}
}
}

UserSDK.default?.notificationDelegate = self

The SDK does not open the push notification link itself. The link value is the exact string entered in the panel. A push notification sent without a link carries the string "None", so treat it as no link. How to route it to a screen, including Universal Links: Opening App Screens.

Tell your own notifications apart​

handleNotification(_:) returns true for a Positive User notification, including in-app messages, and false for any other, which your app handles as usual. isUserComNotification(_:) checks a payload without any other effect.

if UserSDK.default?.handleNotification(userInfo) != true {
// your own notification
}

Show images​

To show the image attached to a push notification, add a Notification Service Extension target and link UserComSDK to it. Then pass the content to NotificationExtensionHelper:

import UserNotifications
import UserComSDK

class NotificationService: UNNotificationServiceExtension {

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

override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
NotificationExtensionHelper.shared.fillNotificationContent(bestAttemptContent, contentHandler: contentHandler)
}

override func serviceExtensionTimeWillExpire() {
if let contentHandler, let bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}

With CocoaPods, add the pod to the extension target as well. UserSDK itself is not available in app extensions. Only NotificationExtensionHelper is.


Methods​

registerForRemoteNotifications(options:notificationDelegate:)​

Asks for notification permission if the user has not answered yet, registers with APNs and makes the SDK the UNUserNotificationCenter delegate. Use it only for Option B.

NameTypeRequiredDescription
optionsUNAuthorizationOptionsYesPermissions to request, for example [.alert, .badge, .sound].
notificationDelegateRemoteNotificationDelegate?NoReceives notification payloads, see notificationDelegate. Default nil.

Returns nothing. The SDK does not fetch the FCM token. Pass it with setFcmToken(_:).

UserSDK.default?.registerForRemoteNotifications(options: [.alert, .badge, .sound], notificationDelegate: self)

handleNotification(_:)​

Passes a received notification to the SDK. For an in-app message, it shows the message. It never counts a click and never calls notificationDelegate. Call it from application(_:didReceiveRemoteNotification:fetchCompletionHandler:), and from userNotificationCenter(_:willPresent:withCompletionHandler:) if you like. Added in 1.3.0.

NameTypeRequiredDescription
userInfo[AnyHashable: Any]YesThe notification payload from iOS.

Returns true for a Positive User notification, false for any other. The result can be ignored. Safe to call from any thread: the SDK moves the work to the main thread.

UserSDK.default?.handleNotification(userInfo)

handleNotificationClick(_:)​

Passes a tapped notification to the SDK. For a Positive User push notification, it reports the click and calls notificationDelegate with the notification ID and the payload. Other notifications go to notificationDelegate with an empty ID. Call it from userNotificationCenter(_:didReceive:withCompletionHandler:). Added in 1.3.0.

NameTypeRequiredDescription
userInfo[AnyHashable: Any]YesThe payload of the tapped notification, response.notification.request.content.userInfo.

Returns nothing. Safe to call from any thread.

UserSDK.default?.handleNotificationClick(response.notification.request.content.userInfo)

isUserComNotification(_:)​

Returns true when the payload is a Positive User notification. It has no other effect. Added in 1.3.0.

let isPositiveUser = UserSDK.default?.isUserComNotification(userInfo) == true

handleNotification(userInfo:)​

Deprecated since 1.3.0, stays until 2.0. It shows in-app messages and counts a click for every other Positive User notification it gets, also for a notification that was only received. Replace it with handleNotification(_:) for received notifications and handleNotificationClick(_:) for taps.

notificationDelegate​

The object that receives the notifications passed to handleNotificationClick(_:), and the taps the SDK handles itself in Option B. In-app messages never reach it. Set it directly or through registerForRemoteNotifications(options:notificationDelegate:).

NameTypeRequiredDescription
notificationDelegateRemoteNotificationDelegate?NoImplements didReceiveRemoteNotification(with:data:).

didReceiveRemoteNotification(with id: String, data: [AnyHashable: Any]) receives the Positive User notification ID (an empty string for other notifications) and the full payload.

UserSDK.default?.notificationDelegate = self

NotificationExtensionHelper.fillNotificationContent(_:contentHandler:)​

Downloads the image from the notification payload, attaches it and calls the content handler. If there is no image or the download fails, it calls the handler with the content unchanged. If content is nil, it does not call the handler at all: call contentHandler(request.content) yourself in that case.

NameTypeRequiredDescription
contentUNMutableNotificationContent?YesThe mutable copy of the notification content.
contentHandler(UNNotificationContent) -> VoidYesThe handler iOS passed to your extension.

Returns nothing. See Show images for a full example.


Common errors​

SymptomCauseFix
The contact cannot be selected in a push campaignThe contact has no device with a push token: no FCM token was passed to the SDK, or the token moved to another contact that later used the same device.See Firebase and FCM Token.
The push notification is sent, but never arrives on the deviceNo APNs key in Firebase, a different Firebase project in the panel, or notifications turned off for the app in the iOS Settings app.Check the APNs key and the Firebase project, then the permission in the iOS Settings app.
Push notifications arrive, but clicks are not countedTaps are not passed to handleNotificationClick(_:), or they are passed to handleNotification(_:), which never counts a click.Call handleNotificationClick(_:) in userNotificationCenter(_:didReceive:withCompletionHandler:), as in Option A.
Clicks are counted for notifications nobody tappedThe app calls the deprecated handleNotification(userInfo:) from willPresent or application(_:didReceiveRemoteNotification:fetchCompletionHandler:).Switch to handleNotification(_:) there.
Your own foreground presentation stopped workingregisterForRemoteNotifications made the SDK the notification center delegate.Use Option A and keep your own delegate.
Tapping a notification does not open the expected screenThe SDK does not open push notification links.Read link in RemoteNotificationDelegate. See Opening App Screens.
Image is not shownNo Notification Service Extension, or the image URL is not HTTPS or is too large.Add the extension and use a public HTTPS image within Apple's attachment size limits.

More cases: iOS Troubleshooting.