Integration

Kumulos provides an SDK in the form of framework to ease integration Kumulos Analytics, Push Notification, Crash Reporting and Build features into your iOS app. This guide provides an overview of setting up the SDK for your project and sample usage.

Integrate SDK components

The Kumulos SDK is an open source project hosted on Github, for ease of use it is linked to from the agency console and can be found at https://github.com/Kumulos/KumulosSdkObjectiveC.

Both Carthage and CocoaPods integration instructions are available in the GitHub repo.

Initialization

To initialize the Kumulos SDK, simply alloc & init the Kumulos class with your application's API key and secret key.

@import KumulosSDK;
...
KSConfig *config = [KSConfig configWithAPIKey:@"YOUR_API_KEY" andSecretKey:@"YOUR_SECRET_KEY"];
[Kumulos initializeWithConfig:config];

A good place to do this is in your AppDelegate application:didFinishLaunchingWithOptions: method.

Checking installs of your App

When you run your app on a simulator or install your app on a device, you can check that the SDK has been initialized correctly by selecting the app and clicking the Installs tab to see the ten most recent installs of your app. Click on any install to see more information.

Recent installs

If you experience any difficulties integrating an SDK or initializing the Kumulos client, please don't hesitate to contact support who are standing by to help!

Installation ID

When initialized for the first time, the Kumulos SDK will create a unique identifier for the app installation that initialized the SDK.

This identifier can be used to target push notifications to a specific device through KScript or the Push Notifications API.

In order to retrieve this installation ID, simply use the class method:

NSString* installId = [Kumulos installId];

Once you have the installation ID, you can send it to your app's backend to be used later for push targeting. For more information about push targeting, please see the KScript Documentation or push notification documentation as appropriate.

Analytics

Session Analytics

Kumulos records sessions based on application foreground and background events. When an app enters the background, Kumulos will wait for a configurable idle period. If there are no more foreground events during that idle period, the current session will be closed.

Session analytics

You can configure the idle period to suit your app's use-case. By default the idle period is 40 seconds of inactivity. The idle period does not count towards session duration calculations.

To adjust the idle period, you can configure the SDK as follows:

KSConfig *config = [KSConfig configWithAPIKey:@"YOUR_API_KEY" andSecretKey:@"YOUR_SECRET_KEY"];
[config setSessionIdleTimeout:120];
[Kumulos initializeWithConfig:config];

Note that due to background task limits, in practise the idle timeout can only reasonably be up to a few minutes

Event Tracking

Kumulos allows you to track custom analytics events that can be used as a trigger to fire automation rules (e.g. to trigger a push notification) or as steps in a funnel to track conversion through key user journeys in your app.

Track conversion

To track a custom analytics event, use trackEvent as follows:

Kumulos.shared trackEvent:@"product.purchased" withProperties:@{
    @"productId": 42
}];

Event tracking is available offline as all events are persisted locally before being synced to the server in batches in the background.

A similar method trackEventImmediately will immediately start an event sync rather than waiting for the next time the app is backgrounded.

Checking events from your App

When you run your app on a simulator or install your app on a device, you can check that the SDK is recording events correctly by selecting the app and clicking the Installs tab to see the ten most recent installs of your app. Click on any install and then click on the Events tab.

Install events

Alternatively, to see counts of all analytics events, including system events such as opened a push notification, recorded by installs of the app in the last 30 days, expand 'Analytics' and click 'Explore' in the left menu. For comparison, the total number of sessions in the same period is shown.

Analytics Explorer

User Association

Kumulos allows associating a user identifier with the current installation ID. This user identifier is useful for performing analytics aggregations & analyses at the user level, for example, funnels completed by users across multiple devices. You can also optionally associate a collection of attributes with the user.

To associate the current app installation with a user, you can use the helper method as shown below:

Kumulos.shared associateUserWithInstall:@"Bob"];

User association is time-aware, so in the case of two different users sharing a device to log in to the app, their user IDs will be associated with the same install for the time between subsequent calls to the helper method. For example:

Kumulos.shared associateUserWithInstall:@"Bob"];

// This event will belong to Bob
Kumulos.shared trackEvent:@"product.purchased" withProperties:@{
    @"productId": 42
}];

Kumulos.shared associateUserWithInstall:@"Alice"];

// This event will belong to Alice
Kumulos.shared trackEvent:@"product.purchased" withProperties:@{
    @"productId": 42
}];

Attributes

You can optionally associate a collection of attributes with the user for targeting, personalization or attribution. To associate attributes with the user, pass a collection into the helper method as shown.

Kumulos.shared associateUserWithInstall:@"Bob" attributes:@{
    @"name": "Shawn",
    @"age": 25
}];

If you do not have a user identifier, use the installation id generated by the Kumulos SDK.

Location Tracking

You can send Kumulos location updates and use this to trigger events such as push notifications when an install enters a GeoFence.

How you configure the Core Location services for iOS depends on the specific use case of your app. You should consider both accuracy and battery life when making this decision. Refer to the Apple documentation for details of how to configure location services.

Once you have created a CLLocationManagerDelegate you can use the helper method in the Kumulos SDK to send location updates to Kumulos.

Kumulos.shared sendLocationUpdate:location];

iBeacon Detection

You can send Kumulos beacon proximity updates and use these to trigger automations such as sending a push notification when an install is in proximity to a beacon.

How you configure the Core Location services for iOS depends on the use case for your app and you should consider throttling how regularly these events are transmitted for both battery life and network traffic.

The Kumulos SDK provides a helper method to notify our services of proximity to a detected beacon.

Kumulos.shared sendiBeaconProximity:beacon];

Messaging & Push Notifications

Kumulos provides powerful multi-channel messaging services to reach your users. By initializing the SDK you can quickly and easily receive both push notifications to re-engage your users as well as feature-rich In-App messages.

Configuration & Integration

To integrate Kumulos Messaging into your iOS project, you have to complete the following steps:

  1. Set up APNS certificates for your app
  2. Configure Push in the Kumulos Agency Console
  3. Integrate SDK components with your iOS project & enable in-app messaging
  4. Register for push form the client app

In order to send push notifications to iOS devices with Kumulos, you'll need to create certificates in the Apple Developer Member Center. The steps to complete this are shown in the video guide.

Configuring APNS for iOS with Kumulos

After configuring your project, continue to follow the steps below.

Enable In-App Messaging

Add the following to your Kumulos config:

@import KumulosSDK;
...
KSConfig *config = [KSConfig configWithAPIKey:@"YOUR_API_KEY" andSecretKey:@"YOUR_SECRET_KEY"];
[config enableInAppMessaging:KSInAppConsentStrategyAutoEnroll];
[Kumulos initializeWithConfig:config];

Next, enable the 'background-fetch' Background Mode in your Xcode project's Capabilities section.

Requesting a Push Token

When you consider it appropriate, you need to request permission from the user to send them push notifications. You can potentially use a Kumulos in-app message to prompt the user to accept push at a later time.

Whilst you can handle this yourself, Kumulos provides a convenience method:

// Assuming you initialized the Kumulos object using [Kumulos initializeWithConfig:]
[Kumulos.shared pushRequestDeviceToken];

This will prompt the user to accept push notifications with the badge, alert, and sound settings. When the user accepts, the Kumulos SDK will store the push token.

To remove the token from Kumulos, call this method:

[Kumulos.shared pushUnregister];

Advanced

Checking Push Registrations

When you run your app on a simulator or install your app on a device, you can check that the install has successfully registered for push notifications by selecting the app and clicking the Installs tab to see the ten most recent installs of your app. Click on any install and then click on Push tab.

Install push details

Supporting Pictures In Notifications

When sending a push notification you can attach a picture to it. It will show on iOS 10+ devices. It will expand upon swiping the notification on devices supporting 3D Touch. In order to enable this functionality you need to add a Notification Service Extension to your application.

Then replace the contents of NotificationService.m with the following lines:


#import "NotificationService.h"
@import KumulosSDK;

@interface NotificationService ()
@end

@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
   [Kumulos didReceiveNotificationRequest:request withContentHandler: contentHandler];
}
@end

Kumulos helper function adds picture attachment to the notification content. You can modify the content before calling didReceiveNotificationRequest or replace the implementation with you own.

If you would like your users to opt-in to receive In-App messages you can configure the SDK during initialization to make opt-in explicit by setting the strategy, then calling the SDK helper to manage their consent.

// Set the strategy to require explicit user consent when initializing the SDK
KSConfig *config = [KSConfig configWithAPIKey:@"YOUR_API_KEY" andSecretKey:@"YOUR_SECRET_KEY"];
[config enableInAppMessaging:KSInAppConsentStrategyExplicitByUser];

// Call this method to update consent based on user preferences / settings screen etc.
[KumulosInApp updateConsentForUser:YES];

Deep-linking for In-App

In-App messages allow you to hand-off to native application screens via deep-linking action buttons. When tapped, these buttons pass control to the defined deep-link handler, including their defined data payload (configured in the In-App message composer for the action button).

If you want to handle deep-links with custom data payloads as part of an In-App message you can add a handler block to your configuration options during SDK initialization.

[config setInAppDeepLinkHandler:^(NSDictionary * _Nonnull data) {
    //- Inspect the data payload and run code as needed.
    NSLog(@"Deep link data: %@", data);
}];

Handling Push Notification Opens for Deep Linking

If you want to implement your own deep links for push notifications, the SDK offers an integrations point which is called when the user opens a push notification.

To set up a handler, simply add the following to your config:

KSConfig *config = [KSConfig configWithAPIKey:@"YOUR_API_KEY" andSecretKey:@"YOUR_SECRET_KEY"];
[config setPushOpenedHandler:^(KSPushNotification* _Nonnull notification) {
    // Inspect notification.data and take action as desired
}];

Note you do not need to handle deep links to in-app messages yourself, the Kumulos SDK already handles these.

Handling Background Data Pushes

When you send a background data push with Kumulos, the content-available flag will be set on the notification.

This will allow your app to be woken up to process the push notification in the background.

Make sure you have enabled the "Remote notifications" background mode in your project's "Capabilities" settings

The background push notification will trigger the application:didReceiveRemoteNotification:fetchCompletionHandler: application delegate:

- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // userInfo[@"aps"][@"content-available"] will be set to 1
    // userInfo[@"custom"][@"a"] will contain any additional data sent with the push

    completionHandler(UIBackgroundFetchResultNoData);
}

Note if you set a title & message then a notification will be shown to the user. If you want the push to be silent, don't set a title or message when sending.

Using the In-App Inbox

In-app messages can optionally be persisted in a user-level inbox for later retrieval. This allows you to build features such as loyalty rewards or expiring coupons into your app.

To retrieve a list of messages from the user's inbox and present the first in the list, see the following example:

NSArray<KSInAppInboxItem*>* items = [KumulosInApp getInboxItems];
[KumulosInApp presentInboxMessage:items[0]];]

Channels

You can create and manage subscriptions to push notification Channels via the SDK. Channels allow your users to define preferences for the type of content they would like to receive via notifications.

You can manage all channel interactions via the KumulosPushSubscriptionManager class.

KumulosPushSubscriptionManager* manager = [[KumulosPushSubscriptionManager alloc] initWithKumulos:Kumulos.shared];

The push manager exposes several methods to manage subscriptions:

@interface KumulosPushSubscriptionManager

- (void) subscribeToChannels:(NSArray<NSString*>* _Nonnull) uuids onComplete:(KSPushSubscriptionCompletionBlock) complete;

- (void) unsubscribeFromChannels:(NSArray<NSString*>* _Nonnull) uuids onComplete:(KSPushSubscriptionCompletionBlock) complete;

- (void) setSubscriptions:(NSArray<NSString*>* _Nonnull) uuids onComplete:(KSPushSubscriptionCompletionBlock) complete;

- (void) clearSubscriptions:(KSPushSubscriptionCompletionBlock) complete;

- (void) listChannels:(KSPushChannelsSuccessBlock) complete;

- (void) createChannelWithUuid:(NSString* _Nonnull) uuid shouldSubscribe:(BOOL) subscribe name:(NSString* _Nullable) name showInPortal:(BOOL) shownInPortal andMeta:(NSDictionary* _Nullable) meta onComplete:(KSPushChannelSuccessBlock) complete;

@end

Results are handled by passing a block for the operation's completion like so:

[self.manager listChannels:^(NSError * _Nullable error, NSArray<KSPushChannel *> * _Nullable channels) {
    if (error) {
        // Handle any error as you want
        return;
    }

    // Handle channel listing result
}];

When creating channels, the visibility and meta data can be controlled. Please make sure you understand these concepts when creating channels from the SDK.

Build

This section shows various examples of calling your Kumulos API methods using the SDK or the generated bindings, and handling results with blocks or delegates as desired.

To talk to Kumulos, you need three things:

  • your method title (in lowerCamelCase)
  • a method parameters map (field names are also in lowerCamelCase)
  • some response handler code

All networking is performed asynchronously, but your result handling blocks & delegates will always be called back on the application's main thread

Using the SDK

Handling Results with Blocks

To make calls to your API methods, use the callMethod:... selectors on the Kumulos class. For an example using blocks to handle the response:

NSDictionary* params = @{@"author": @"Jane Austin"};
// Assuming Kumulos instance has been initialized as shown in the Initialization section
[Kumulos.shared callMethod:@"getAllBooksByAuthor" withParams:params success:^(KSAPIResponse * _Nonnull response, KSAPIOperation * _Nonnull op) {
    NSArray* books = response.payload;

    // Do something with the books
  } andFailure:^(NSError * _Nonnull err, KSAPIOperation * _Nonnull op) {
    NSLog(@"Something went wrong!");
  }];

Handling Results with Delegates

Alternatively you can handle results using a delegate conforming to the KSAPIOperationDelegate protocol:

@import KumulosSDK;

@interface ViewController () <KSAPIOperationDelegate>

@property (nonatomic) Kumulos* k;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.k = [[Kumulos alloc] initWithAPIKey:@"YOUR_API_KEY" andSecretKey:@"YOUR_SECRET_KEY"];
    [self.k [email protected]"getAllAuthors" withDelegate:self];
}

- (void) operation:(KSAPIOperation *)operation didCompleteWithResponse:(KSAPIResponse *)response {
    NSString* methodAlias = operation.method;
    NSLog(@"%@", response.payload);

    // Check which method it was and handle result as appropriate
}

// Optionally conform to handle errors
- (void) operation:(KSAPIOperation *)operation didFailWithError:(NSError *)error {
    NSLog(@"Something went wrong!");
}

@end

Cancelling API Operations

To cancel requests, just grab the KSAPIOperation* from the Kumulos object and call the cancel selector. For example:

KSAPIOperation* theOperation = [Kumulos.shared callMethod:@"myNotSoAwesomeOperation" withDelegate:self];
// Change your mind
[theOperation cancel];

Using the Generated Bindings

If you are using the generated bindings, each of your API methods will be available to invoke directly on the Kumulos class thanks to the Kumulos (Bindings) category implemented by Kumulos.m.

Integration

Using the generated bindings assumes that you have deployed an API for iOS/OSX and downloaded the libraries.

After downloading the libraries, to make use of the generated bindings, drag & drop the Kumulos.h and Kumulos.m files from the download into your project.

Add the files to your project

Finally, you should install the KumulosSDK core framework from CocaPods, Carthage, or sources (instructions on GitHub).

Initialization

After integration, you can use the generated init method that has the keys provided:

#import "Kumulos.h"
...
Kumulos* k = [[Kumulos alloc] init];

Example

In this mode of operation, you can only handle the reults using a delegate conforming to the KumulosDelegate protocol defined in Kumulos.h.

For example:

#import "Kumulos.h"

@interface ViewController () <KumulosDelegate>

@property (nonatomic) Kumulos* k;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.k = [[Kumulos alloc] initWithAPIKey:@"YOUR_API_KEY" andSecretKey:@"YOUR_SECRET_KEY"];

    // Tell Kumulos which delegate will handle API operation results
    [self.k setDelegate:self];
    // Invoke the API method directly
    [self.k getAllAuthors];
}

- (void) kumulosAPI:(Kumulos*)kumulos apiOperation:(KSAPIOperation*)operation getAllAuthorsDidCompleteWithResult:(NSArray*)theResults {
    // Handle the results for the getAllAuthors API call
}

// Optionally handle errors
- (void) kumulosAPI:(kumulosProxy *)kumulos apiOperation:(KSAPIOperation *)operation didFailWithError:(NSString *)theError {
    ...
}

@end

Crash

Crash reporting allows you to track unhandled exceptions in your app, and optionally log any caught exceptions for further investigation. Crash reporting is not enabled by default, to enable this feature, simply modify your Kumulos initialization to include enabling crash reporting.

KSConfig *config = [KSConfig configWithAPIKey:@"YOUR_API_KEY" andSecretKey:@"YOUR_SECRET_KEY"];
[config enableCrashReporting];
[Kumulos initializeWithConfig:config];

Unhandled exceptions that lead to crashes will now be recorded automatically and shown in your App Delivery Console.

Note that crash reporting is not available whilst connected to a debugger, but it does work with debug builds.

If you wish to log any caught exceptions, you can record them with the helper method:

[Kumulos.shared logExceptionWithName:@"name" reason:@"reason" language:@"objc" lineNumber:@"1" stackTrace:@[@"boom", @"clunk", @"bang"] loggingAllThreads:YES];

Ensure that crash reporting is enabled when attempting to log exceptions.

Upload dSYM Files for Symbolication

In order to ensure your stack traces are fully symbolicated, it is necessary to upload your dSYM information from your project.

To upload dSYM files for Release builds, you should add a new "Run Script Phase" to your Xcode project's "Build Phases".

For CocoaPods installations, use this script:

"$PODS_ROOT/KumulosSdkObjectiveC/upload_dsyms.sh" API_KEY SERVER_KEY

For Carthage installations, use this script:

"$SRCROOT/Carthage/Checkouts/KumulosSdkObjectiveC/upload_dsyms.sh" API_KEY SERVER_KEY

Copy the script snippet into a new Run Script Phase as shown below:

Add a new Run Script Build Phase

Make sure you replace the API_KEY and SERVER_KEY tokens with your app's API and server keys which can be found on the application's dashboard.

Bitcode-enabled Builds

If your project has bitcode enabled, it will be necessary to upload the dSYM files that Apple generates as part of App Store distribution step.

These generated dSYM files can be downloaded using the Xcode Organizer. Once downloaded, the generated dSYM files will be available in your app's archive.

You should follow the steps of the screen capture below to prepare your symbols for upload.

Fetching & preparing generated dSYMs for upload

Once prepared for upload, you should upload the dSYM zip file to your App Delivery Console.

Changelog

2.3.0

  • Support pictures in push notifications

2.0.0

  • Add in-app messaging support
  • Handle all push lifecycle & notification delegates out the box

Upgrade Guide

If you are not using push notifications, you don't need to do anything to update.

All relevant changes are related to the setup of push notification delegates. As part of the in-app messaging feature, the Kumulos SDK needs more control of these delegates.

As such, the SDK now handles the following delegates for you:

  • application:didRegisterForRemoteNotificationsWithDeviceToken:
  • application:didFailToRegisterForRemoteNotificationsWithError:
  • application:didReceiveRemoteNotification:fetchCompletionHandler:
  • application:performFetchWithCompletionHandler:
  • UNUserNotificationCenterDelegate

All behaviors related to correctly persisting the push token, tracking notification opens, opening URL pushes, etc. are now handled by the SDK, making integration simpler.

This means it is safe to remove all delegate setups listed above if you previously followed the base Kumulos integration guide for their setup and added no further customization.

If you did implement any of the above application delegates and customize their behavior, the Kumulos SDK will still call them first, and then continue handling the logic required for Kumulos. The only exception to this is the UNUserNotificationCenterDelegate which does not call to any existing delegate.

Note that it is important to invoke the completionHandler callbacks from all delegate methods in a timely fashion.

In addition, it is necessary to add the background-fetch background mode to your app's capabilities in the Xcode project setup.

If you want to continue handling push open behavior yourself, you can now set the push notification opened handler block in the KSConfig class with setPushOpenedHandler method.

1.8.0

  • Add new helper method for device token removal

1.7.0

  • Persist userIdentifier to NSUserDefaults on association and add accessor
  • Record current userIdentifier with events and send in server sync
  • Add helper to clear any existing user association
  • Create analytics data model in code
  • Ensure correct thread usage for CoreData operations

1.6.8

  • Allow overriding target type for use in SDKs where sources are not used

1.6.7

  • Remove AFNetworking dependency to allow use in static libraries
  • Add static library target to allow embedding the SDK in React Native
  • Increase priority for analytics task dispatch (background priority can be starved)

1.6.6

  • Don't sync events which have not been fully persisted yet

1.6.5

  • Add support for storing user attributes when associating an install with a user

1.6.4

Use quoted header import style for CocoaPods usage

Works around an issue with Cordova plugin usage from a pod where headers can't be resolved using the system style imports.

1.6.3

  • Expose helper to allow tracking event then flushing events to the server
  • Allow configuring SDK & runtime info for use in wrapper SDKs
  • Early return from event tracking when properties are not serializable

1.6.2

  • Remove use of NSBatchDeletionRequest to support iOS < 9
  • Drop required iOS deployment target to 8.0
  • Fix availability warning by using @available guard
  • Enable availability warnings for all versions

1.6.1

  • Don't import using the module syntax to allow usage from non-framework pod targets
  • Remove unnecessary framework links and set headers dir for consistent include statements
  • Update import statements and remove use_frameworks! note

1.6.0

  • Add iBeacon proximity helper
  • Refactor most server interactions to be recorded in the events DB
  • Fix Podspec missing resources file

1.5.3

  • Add dSYM upload script to SDK distribution

1.5.3

  • Record background event synchronously

1.5.1

  • Prevent intermittent build failures by explicitly referencing dependency targets from the SDK schemes

1.5.0

  • Add custom event tracking
  • Capture session analytics events
  • Add user association helper

1.4.1

  • Add public header file to Podspec

1.4.0

  • Add Crash Reporting

1.3.2

  • Version bump

1.3.1

  • Expose geolocation update helper publicly
  • Fix nil reference exception in stats

1.3.0

  • Add geolocation update helper method
  • Add locale & bundle to stats payload

1.2.1

  • Add missing header guard for macOS

1.2.0

  • Add Podspec for CocoaPods support
  • Update installation instructions
  • Fix include guards for iOS & macOS headers

1.1.0

  • Add push channel subscription management

1.0.1

  • Fix compilation of framework to include full bitcode

1.0.0

  • Initial release of SDK as a framework
  • Initial release of support for Kumulos Push
  • Clean up of public interfaces & properties
  • Move supporting behavior for generated bindings from the core SDK to the generated bindings
  • Modernize Objective-C throughout
  • Utilize latest AFNetworking throughout

The generated bindings code has been updated to be compatible with our latest SDK whilst aiming to maintain backwards compatibility with the most commonly used features of the older SDKs. However, if you have any trouble upgrading, please drop a line to [email protected] -- we'd be happy to help.