ProjectorKit Integration Guide

Overview

This guide shows you how to use the ProjectorKit SDK for iOS and implement its features in your own application code.

Using the ProjectorKit Dependency in your Application

XCode Configuration

TODO - decide how to provision ProjectorKit - binary or open source repo

Adding ProjectorKit to your Application Code

Follow these steps to get up and running using the API defined by the Projector class.

Acquiring your application’s token

In order to use the ProjectorKit in your application, you’ll need an application token.

This is provisioned through the Projector Dashboard. TODO - get screenshots etc

It is referred to in ProjectorKit as the variable or function argument appToken.

Importing ProjectorKit

In all of the following sections you’ll need to ensure that you have the Projector class in scope by importing ProjectorKit.

Find the list of imports near the top of your *.swift files and be sure to add the following line in that section.

import ProjectorKit

UIKit Delegate Class Modifications

You’ll need to modify or create two implementations of delegate interfaces from UIKit.

In the next subsections you’ll find detailed instructions on exactly what to add and where to put it.

UIApplicationDelegate and UNUserNotificationCenterDelegate Modifications

If you already have your UIApplicationDelegate implementation class defined, adding the necessary Projector class hooks is easy.

If you need to create your UIApplicationDelegate class, please see the documentation here from Apple.

We recommend that you also implement your UNUserNotificationCenterDelegate in the same class. Please see the documentation here.

Add the following lines to your UIApplicationDelegate class:

First find, or create, the method application(_:didFinishLaunchingWithOptions:)

Inside this method add the following line anywhere before your return statement, replacing the appToken argument $MyApplicationToken with the application token you retrieved from the Projector Dashboard:

Projector.shared.initUserNotifications(appToken: $MyApplicationToken, delegate: self)

An example implementation might look like this if your class implementing UIApplicationDelegate also implements the UNUserNotificationCenterDelegate protocol:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    // note below: self refers to this class as its implementing UNUserNotificationCenterDelegate as well
    Projector.shared.initUserNotifications(appToken: "OurApplication-2222224848000", delegate: self)
    return true
}

If you’re implementing your UNUserNotificationCenterDelegate in another place, you’ll need to pass in that object into this method as the delegate.


Then you need to add in application user notification registration handlers as follows.

Find, or create, the method application(_:didRegisterForRemoteNotificationsWithDeviceToken:)

Inside this method we need to pass the device token to Projector. Add the following line anywhere you like inside this method:

Projector.shared.deviceToken = deviceToken

An example implementation might look like this:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Projector.shared.deviceToken = deviceToken
    NSLog("\(#function) - Device Token - \(Projector.shared.pushToken)")
}

Then, find or create, the method application(_:didFailToRegisterForRemoteNotificationsWithError:)

Inside this method we handle the case that the application failed to register with APNS. We simply assign an empty string to the Projector pushToken variable.

For example:

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    NSLog("\(#function) - Error - \(error.localizedDescription)")
    #if (arch(i386) || arch(x86_64)) && os(iOS)
        Projector.shared.pushToken = ""
    #endif
}

Then you need to wire up the various application lifecycle callback methods from UIApplicationDelegate.


Find, or create, the method applicationDidEnterBackground(_:)

Inside this method add the following line anywhere you like:

Projector.shared.applicationDidEnterBackground()

An example implementation might look like:

func applicationDidEnterBackground(_ application: UIApplication) {
    Projector.shared.applicationDidEnterBackground()
}

Find, or create, the method applicationWillEnterForeground(_:)

Inside this method add the following line anywhere you like:

Projector.shared.applicationWillEnterForeground()

An example implementation might look like:

func applicationWillEnterForeground(_ application: UIApplication) {
    Projector.shared.applicationWillEnterForeground()
}

Find, or create, the method applicationDidBecomeActive(_:)

Inside this method add the following line anywhere you like:

Projector.shared.applicationDidBecomeActive()

An example implementation might look like:

func applicationDidBecomeActive(_ application: UIApplication) {
    Projector.shared.applicationDidBecomeActive()
}

Find, or create, the method applicationWillTerminate(_:)

Inside this method add the following line anywhere you like:

Projector.shared.applicationWillTerminate()

An example implementation might look like:

func applicationWillTerminate(_ application: UIApplication) {
    Projector.shared.applicationWillTerminate()
}

Now we need to add a means to intercept background (content-available) remote notifications.

In the case that you’ve already implemented content-available style background notification handling in your application, find the method application(_:didReceiveRemoteNotification:)

If you haven’t implemented this or don’t wish to use content-available notifications, enabling many powerful Projector features, skip this step.

If you want to implement the method, an example Projector integration merely involves adding the following lines anywhere inside your method:

guard let request = UNNotificationRequest(withRemoteNotification: userInfo) else {
    completionHandler(.noData)
    return
}
Projector.shared.didReceiveRemoteNotification(request: request)

where the most basic implementation - with no other special support inside your application, might look like this:

@nonobjc func application(_ application: UIApplication,
                          didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                          fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
    guard let request = UNNotificationRequest(withRemoteNotification: userInfo) else {
        completionHandler(.noData)
        return
    }
    NSLog("Intercepting background push")
    Projector.shared.didReceiveRemoteNotification(request: request)
    completionHandler(.noData)
}

Setting your User Notification Categories

If your application supports user notification categories (as in UNNotificationCategory), you must pass these to the Projector object in order to support Projector features.
If your application doesn’t define any such categories, you don’t need to do anything outside of the initUserNotifications hook call.

It is recommended that you pass your user notification categories to the Projector object in the applicationDidBecomeActive(application:) method inside your UIApplicationDelegate class.

However, you are free to call the setNotificationCategories(_:) method whenever you desire.

Add the following lines of code inside that method, where $aReferenceToMyAppNotificationCategorySet is a reference to your set of categories object:

let appNotificationCategories: Set<UNNotificationCategory> = $aReferenceToMyAppNotificationCategorySet
Projector.shared.setNotificationCategories(appNotificationCategories)

An example implementation might look like this:

func applicationDidBecomeActive(_ application: UIApplication) {
    Projector.shared.setNotificationCategories(appNotificationCategories) // example: appNotificationCategories here is referenced from a field in your class
    Projector.shared.applicationDidBecomeActive()
}

Setting your Unique User Identification String

If you with to identity your users in Projector with your unique application user identification string, the logic is similar to setting the user notification categories section above.

It is recommended that you pass your unique user id to the Projector object in the applicationDidBecomeActive(application:) method inside your UIApplicationDelegate class.

However, you are free to call the setUserId(id:) method whenever you desire.

Add the following lines of code inside that method, where $aReferenceToTheUserId is a reference to your set of categories object:

let userId: String = $aReferenceToTheUserId
Projector.shared.setUserId(id: userId)

An example implementation might look like this:

func applicationDidBecomeActive(_ application: UIApplication) {
    Projector.shared.setUserId(id: userId) // example: userId here is referenced from a field in your class
    Projector.shared.applicationDidBecomeActive()
}

Capturing Custom Application Events

ProjectorKit enables the capture of metadata surrounding custom application events. For example, your application might have logic wherein a user performs a transaction that you’d like to measure in relation to other application lifecycle behaviors, such as opening an application or receiving a push notification.

In order to achieve this measurement using ProjectorKit all you need to do is add the following method to the appropriate place in your application:

let name: String = $nameOfMyCustomAction
let metadata: [String: String] = $customActionMetadataDictionary
Projector.shared.customAction(name: name, metadata: metadata)

For example, imagine you had a method called in your application when a shopping cart was purchased:

func shoppingCartPurchased(withCount itemCount: Int, forPrice price: Dollars, serverNotification callback: () -> Void) -> Void {
    Projector.shared.customAction(name: "shoppingCartPurchased", metadata: ["itemCount": "\(itemCount)", "price": "\(price)"])
    callback
}

The metadata dictionary can be empty.

You are free to add this in as many places as you’d like in your application code.