Skip to content

Customer IO

In your AppDelegate.swift, the following changes are needed:

didFinishLaunchingWithOptions:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
{
  // Update the CustomerIO profile attribute to set if background processing is allowed
  // (replace this with your existing `identify` call, if you have this somewhere else, that is okay also)
  CustomerIO.shared.identify(userId: email, traits: ["ctx_background_push_allowed": UIApplication.shared.backgroundRefreshStatus == .available])

  // If you don't already have this line
  UNUserNotificationCenter.current().delegate = self

  // Your other code here

  return true
}
userNotificationCenter:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) 
{
  let userInfo = response.notification.request.content.userInfo
  // You can call this for every notification, we automatically filter out the ones not relevant
  ContextManager.handleNotificationClicked(userInfo: userInfo)
  completionHandler()
}
didReceiveRemoteNotification:
func application(_ application: UIApplication, 
                     didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
{
  // Update the CustomerIO profile attribute "ctx_last_processed" to be the current date
  CustomerIO.shared.identify(userId: email, traits: ["ctx_last_processed": round(Date().timeIntervalSince1970)])

  if application.applicationState == .background || application.applicationState == .inactive {
    if let title = userInfo["ctx_title"] as? String,
       let body = userInfo["ctx_body"] as? String,
       let collapseId = userInfo["ctx_collapse_id"] as? String,
       let flowName = userInfo["ctx_flow_name"] as? String,
       let notificationDeadlineH = userInfo["ctx_notification_deadline_h"] as? Int
    {
      let pushContent = UNMutableNotificationContent()
      pushContent.title = title
      pushContent.body = body
      ContextManager.deliverNotificationIfGoodMoment(
          flowName: flowName,
          collapseId: collapseId,
          notificationContent: pushContent,
          notificationDeadlineH: notificationDeadlineH,
          completionHandler: completionHandler
      )
    } else {
      print("Error parsing userInfo of notification: \(userInfo)")
      completionHandler(.failed)
    }
  } else {
    // Your app is already running, you can still decide to show the notification
  }
}

Enable Remote Notification entitlement

In your Xcode project, go to Signing & Capabilities and enable the Remote Notification capability in the Background Modes section.

Update your push notification payload

Update your push notification payload of your push notification provider to include the following details:

{
  "aps": {
    "alert": { 
    },
    "content-available": 1
  },
   "ctx_title": "Daily Reminder",
   "ctx_body": "Get back to learning, {{ customer.email }} 4",
   "ctx_flow_name": "daily_reminder",
   "ctx_collapse_id": "{{ 'now' | date: "%B %-d, %Y" }}",
   "ctx_notification_deadline_h": 12
}

Explanation of the fields:

  • ctx_title & ctx_body: The title and body of the notification. This isn't used by ContextSDK directly, but is used by you in the didReceiveRemoteNotification method to create the UNMutableNotificationContent. You can add any other fields you need to create your notification (e.g. attachments, sound, etc.)
  • 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 each ctx_flow_name you use in your app
  • ctx_collapse_id: The collapse ID of the notification, which is used to only show a single notification per ctx_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.
  • ctx_notification_deadline_h: The time span in which the notification should be shown in hours. The latest time the notification will be shown is ctx_notification_deadline_h hours after the first background push notification was received on the users device. Keep in mind that this still doesn't guarantee the user will see the notification.

For example, if you want to leverage context-aware push notifications for your daily reminders, you should use the current date as the ctx_collapse_id and the ctx_flow_name to daily_reminder. If your logic is to show the notification anytime between 9am to 9pm, you'd set ctx_notification_deadline_h to 12 and start sending the notifications hourly at 9am.

Things to consider

  • The message will be delayed by around 5 seconds, due to the app detecting the real-world context
  • 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