OneSignal
Enable Remote Notification
entitlement
In your Xcode project, go to Signing & Capabilities
and enable the Remote Notification
capability in the Background Modes
section.
Create all Segments
This segment contains all users who have recently processed a background push notification (ctx_last_processed
) and who have background push enabled (ctx_background_push_allowed
).
This segment contains all users for which we should show a push notification, based on ContextSDK's prediction.
This segment contains all users who have seen a context-aware notification today (assuming you want to send a daily reminder to your users).
Create all Templates
This template is used for traditional push notifications that are not context-aware. This will be used as a fallback if the context-aware notification can't be shown. You can customize the content in this template as you like. Be sure to set ctx_context_aware
to false
.
This template is used to show a context-aware notification to the user for when it's a good moment to show the notification. You can customize the content in this template as you like. Be sure to set ctx_context_aware
to true
.
This template is used to wake up your app briefly to check if it's a good moment. The user won't see this notification. It's critical that you set the Content Available
flag.
This template is used to clear any pending background push notifications before doing a fallback to a traditional push notification. This is a safety net to ensure that the user doesn't see the same notification twice.
Create all Journeys
This journey is triggered by the iOS app when the ctx_collapse_id
and ctx_flow_name
user tags are set, to then trigger actually showing the push notification.
This journey is triggered daily, and distinguishes between showing a context-aware notification or a traditional push notification depending on the user's segment.
At the end of the day, if the user hasn't seen a context-aware notification, a traditional push notification will be sent.
Update your AppDelegate
In your AppDelegate.swift
, the following changes are needed:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
// Let OneSignal know if background push is enabled for this device
OneSignal.User.addTag(key: "ctx_background_push_allowed", value: UIApplication.shared.backgroundRefreshStatus == .available ? "true" : "false")
// If you don't already have this line
UNUserNotificationCenter.current().delegate = self
// Your other code here
return true
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
{
let userInfo = response.notification.request.content.userInfo
if let custom = userInfo["custom"] as? [String: Any],
let additionalData = custom["a"] as? [String: Any],
var collapseId = additionalData["ctx_collapse_id"] as? String
{
if collapseId == "daily_reminder" {
collapseId += "-\(dateFormatter.string(from: Date()))"
} else { /* This is a one-off campaign */ }
// You can call this for every notification, we automatically filter out the ones not relevant
ContextManager.handleNotificationClicked(userInfo: userInfo, customCollapseId: collapseId)
}
completionHandler()
}
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
{
// Update the OneSignal profile tag "ctx_last_processed" to be the current timestamp
OneSignal.User.addTag(key: "ctx_last_processed", value: String(Int(Date().timeIntervalSince1970)))
if application.applicationState == .background || application.applicationState == .inactive {
if let custom = userInfo["custom"] as? [String: Any],
let additionalData = custom["a"] as? [String: Any],
var collapseId = additionalData["ctx_collapse_id"] as? String,
let flowName = additionalData["ctx_flow_name"] as? String
{
if collapseId == "daily_reminder" {
collapseId += "-\(dateFormatter.string(from: Date()))"
} else { /* This is a one-off campaign */ }
ContextManager.checkIfGoodMomentForPushNotification(
flowName: flowName,
collapseId: collapseId) { pushOutcome in
if pushOutcome == .shouldShowPush {
OneSignal.User.addTags([
"ctx_collapse_id": collapseId,
"ctx_flow_name": flowName
])
}
completionHandler(.newData)
}
} else {
print("Error parsing userInfo of notification: \(userInfo)")
completionHandler(.failed)
}
} else {
// Your app is already running, you can still decide to show the notification
}
}
Explanation of the fields:
ctx_flow_name
: The name of the flow (e.g.daily_reminder
) describing what type of notification this is. You will get a custom model for eachctx_flow_name
you use in your appctx_collapse_id
: The collapse ID of the notification, which is used to only show a single notification perctx_collapse_id
at the perfect time. For a daily reminder you'd change this every day, for re-engagement campaigns you may change this every time you change your content.
Things to consider
- The message will be delayed by around 5 seconds, due to the app detecting the real-world context, and one round-trip of network requests
- Due to the nature of iOS push notifications, there is no guarantee that your app gets woken up for every notification. We recommend setting a
ctx_last_processed
attribute in your user profile to track the last time a notification was processed. This way, you can fallback to static notifications if the user hasn't opened the app in a while.
Go Live
Now all that's left is to ship your update to the App Store to start calibrating your model