Initial SDK Setup

Installing the Appboy SDK will provide you with basic analytics functionality as well as a working in-app slideup message with which you can engage your users.

The iOS Appboy SDK should be installed or updated using CocoaPods, a dependency manager for Objective-C and Swift projects. CocoaPods provides added simplicity for integration and updating.

Time Estimate: 5-10 Minutes

iOS SDK CocoaPod Integration

Step 1: Install CocoaPods

Installing the SDK via the iOS CocoaPod automates the majority of the installation process for you. Before beginning this process please ensure that you are using Ruby version 2.0.0 or greater. Don’t worry, knowledge of Ruby syntax isn’t necessary to install this SDK.

Simply run the following command to get started:

$ sudo gem install cocoapods

Note: If you are prompted to overwrite the rake executable please refer to the Getting Started Directions on CocoaPods.org for further details.

Note: If you have issues regarding CocoaPods, please refer to the CocoaPods Troubleshooting Guide.

Step 2: Constructing the Podfile

Now that you’ve installed the CocoaPods Ruby Gem, you’re going to need to create a file in your Xcode project directory named Podfile.

If you are using Xcode 8+, add the following line to your Podfile:

target 'YourAppTarget' do
  pod 'Appboy-iOS-SDK'
end

If you are using Xcode 7 or earlier, please use SDK version 2.23.

Note: We suggest you version Appboy so pod updates automatically grab anything smaller than a minor version update. This looks like ‘pod ‘Appboy-iOS-SDK’ ~> Major.Minor.Build’. If you want to integrate the latest version of Appboy SDK automatically even with major changes, you can use pod 'Appboy-iOS-SDK' in your Podfile.

Note: If you do not use any Appboy default UI and don’t want to introduce the SDWebImage dependency, please point your Appboy dependency in your Podfile to our Core subspec, like pod 'Appboy-iOS-SDK/Core' in your Podfile. .

Example Podfile

If you would like to see an example, see the Podfile within our Stopwatch Sample Application. If you use user_frameworks! in your Podfile, please see the Podfile within our HelloSwift Sample Application.

Step 3: Installing the Appboy SDK

To install the Appboy SDK Cocoapod, navigate to the directory of your Xcode app project within your terminal and run the following command: pod install

At this point you should be able to open the new Xcode project workspace created by CocoaPods.

New Workspace

Step 4: Updating your App Delegate

Add the following line of code to your AppDelegate.m file:

#import "Appboy-iOS-SDK/AppboyKit.h"

Within your AppDelegate.m file, add the following snippet within your application:didFinishLaunchingWithOptions method:

[Appboy startWithApiKey:@"YOUR-API-KEY"
         inApplication:application
     withLaunchOptions:launchOptions];

If you are integrating the Appboy SDK with Cocoapods or Carthage, add the following line of code to your AppDelegate.swift file:

#import Appboy_iOS_SDK

For more information about using Objective-C code in Swift projects, please see the Apple Developer Docs.

In AppDelegate.swift, add following snippet to your application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool:

Appboy.startWithApiKey("YOUR-API-KEY", inApplication:application, withLaunchOptions:launchOptions)

Note: Appboy’s sharedInstance singleton will be nil before startWithApiKey: is called, as that is a prerequisite to using any Appboy functionality.

Note: Be sure to update YOUR-API-KEY with the correct value from your App Settings page.

Note: Be sure to initialize Appboy in your application’s main thread.

Implementation Example

See the AppDelegate.m file in the Stopwatch sample app.

SDK Integration Complete

Appboy should now be collecting data from your application and your basic integration should be complete. Please see the following sections in order to enable custom event tracking, push messaging, the news-feed and the complete suite of Appboy features.

Updating the Appboy SDK via CocoaPods

To update a Cocoapod simply run the following commands within your project directory:

pod update

Customizing Appboy On Startup

If you wish to customize Appboy on startup, you can instead use the Appboy initialization method startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions and pass in an optional NSDictionary of Appboy startup keys.

In your AppDelegate.m file, within your application:didFinishLaunchingWithOptions method, add the following Appboy method:

[Appboy startWithApiKey:@"YOUR-API-KEY"
          inApplication:application
      withLaunchOptions:launchOptions
      withAppboyOptions:appboyOptions];

In AppDelegate.swift, within your application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool method, add the following Appboy method:

Appboy.startWithApiKey("YOUR-API-KEY", inApplication:application, withLaunchOptions:launchOptions, withAppboyOptions:appboyOptions)

where appboyOptions is a Dictionary of startup configuration values.

Note: This method would replace the startWithApiKey:inApplication:withLaunchOptions: initialization method from above.

This method is called with the following parameters:

  • YOUR-API-KEY – Your application’s API Key from the Appboy Dashboard
  • application – The current app
  • launchOptions – The options NSDictionary that you get from application:didFinishLaunchingWithOptions:
  • appboyOptions – An optional NSDictionary with startup configuration values for Appboy

See Appboy.h for a list of Appboy startup keys.

Appboy.sharedInstance() and Swift nullability

Differing somewhat from common practice, the Appboy.sharedInstance() singleton is optional. The reason for this is that, as noted above, sharedInstance is nil before startWithApiKey: is called, and there are some non-standard but not-invalid implementations in which a delayed initialization can be used.

If you call startWithApiKey: in your didFinishLaunchingWithOptions: delegate before any access to Appboy’s sharedInstance (the standard implementation), you can use optional chaining, like Appboy.sharedInstance()?.changeUser("testUser"), to avoid cumbersome checks. This will have parity with an Objective-C implementation that assumed a non-null sharedInstance.

Carthage Integration

You can integrate using Carthage by including the following in your Cartfile:

github "Appboy/Appboy-iOS-SDK" "2.30.0"

Once you’ve synced the Appboy SDK release artifacts (we support Carthage via a zip of release artifacts attached to our Github releases), integrate the Appboy_iOS-SDK.framework and SDWebImage.framework into your project. Then, in your Application delegate do:

#import <Appboy_iOS_SDK/AppboyKit.h>

...

// In `application:didFinishLaunchingWithOptions:`
[Appboy startWithApiKey:@"YOUR-API-KEY"
         inApplication:application
     withLaunchOptions:launchOptions];

Manual Integration Options

Note: We strongly recommend that you implement the SDK via a CocoaPod. It will save you a lot of time and automate much of the process for you. However, if you are unable to do so you may complete integration manually without CocoaPods by following our manual integration instructions on the following page:

Optional IDFA Collection

IDFA Collection is optional within the Appboy SDK and disabled by default. IDFA Collection is required if you intend to utilize our install attribution integrations. However, we may develop additional features in the future which would benefit from the collection of your IDFA. If you opt to store your IDFA, we will store it free of charge so you may take advantage of these options immediately upon release without additional development work.

As a result, we recommend continuing to collect the IDFA if you meet any of the following criteria:

  • You are using advertising elsewhere in the app or through our in-app News Feed
  • You are attributing app installation to a previously served advertisement
  • You are attributing an action within the application to a previously served advertisement

IDFA collection is enabled by implementing the ABKIDFADelegate protocol. Please check IDFADelegate for a full sample code.

iOS SDK Size

Appboy measures the size of our iOS SDK by observing the SDK’s effect on .ipa size, per Apple’s recommendations on app sizing. If you are calculating the iOS SDK’s size addition to your application, we recommend following the steps under “Getting an App Size Report” to compare the size difference in your .ipa before and after integrating the Appboy iOS SDK. When comparing sizes from the App Thinning Size Report, we also recommend looking at app sizes for thinned .ipa files, as universal .ipa files will be larger than the binaries downloaded from the App Store and installed onto user devices.

Note: If you are integrating via Cocoapods with use_frameworks!, set Enable Bitcode = NO in target’s Build Settings for accurate sizing.

Push Notifications

Integration

A push notification is an out-of-app alert that appears on the user’s screen when an important update occurs. Push notifications are a valuable way to provide your users with time-sensitive and relevant content or to re-engage them with your app.

Sample push notification:

Sample Push iOS

For more information and best practices on push, visit our Appboy Academy page.

Basic Push Integration

Time Estimate: 7 Minutes

Step 1: Configure the Apple Developer Settings

  1. Navigate to the iOS Provisioning Portal
  2. Select Identifiers > App IDs in the left sidebar

iOSPush3

  1. Select your application
  2. If push notifications are not enabled, click Edit to update the app settings AppleProvisioningOptions
  3. Tick the Enable check box and click Create Certificate under the Production SSL Certificate iOSPush3
  4. Follow the instructions from the SSL certificate assistant. You should now see a green status to indicate that push is enabled. Note: You must update your provisioning profile for the app after you create your SSL certificates. A simple “Refresh” in the organizer will accomplish this.

Step 2: Export Your Push Certificate

  1. Download the production push certificate that you just created and open it with the Keychain Access application
  2. In Keychain Access, click on My Certificates and locate your push certificate
  3. Export it as a .p12 file and use a temporary, unsecure password (you will need this password when uploading your certificate to Appboy)
  4. Navigate to the app settings page in the dashboard and upload your production certificate.

push upload example

Note: You can upload either your development or production push certificates to the dashboard for your distribution provisioning profile apps, but you can only have one active at a time. As such, if you wish to do repeated testing of push notifications once your app goes live in the App Store, we recommend setting up a separate App Group or App for the development version of your app.

Step 3: Enable Push Capabilities

In your project settings, please ensure that under the Capabilities tab your Push Notifications capability is toggled on. enable push notification

Note: If you are using Xcode 8 and have separate development and production push certificates, please make sure to uncheck the Automatically manage signing box in the General tab. This will allow you to choose different provisioning profiles for each of your build configurations, as Xcode’s automatic code signing feature only does development signing. xcode 8 auto signing

Step 4: Register for Push Notifications

The appropriate code sample below must be included within your app’s application:didFinishLaunchingWithOptions: delegate method for your users’ device to register with APNs.

Appboy also provides default push categories for push action button support, which must be manually added to your push registration code. See our push action buttons documentation for additional integration steps.

Note: If you’ve implemented a custom push prompt as described in our push best practices, make sure that you’re calling the following code EVERY time the app runs after they grant push permissions to your app. Apps need to re-register with APNs as device tokens can change arbitrarily.

Using UserNotification Framework (iOS 10+)

If you are using the UserNotifications framework (recommended) that was introduced in iOS 10, use the following code:

if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) {
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;
  [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge)
                        completionHandler:^(BOOL granted, NSError * _Nullable error) {
                          [[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted];
  }];
  [[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
  UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:nil];
  [[UIApplication sharedApplication] registerForRemoteNotifications];
  [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
if #available(iOS 10, *) {
  let center = UNUserNotificationCenter.current()
  center.delegate = self
  center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
    print("Permission granted.")
  }
  UIApplication.shared.registerForRemoteNotifications()
} else {
  let types : UIUserNotificationType = [.alert, .badge, .sound]
  var setting : UIUserNotificationSettings = UIUserNotificationSettings(types:types, categories:nil)
  UIApplication.shared.registerUserNotificationSettings(setting)
  UIApplication.shared.registerForRemoteNotifications()
}
iOS 8+ without UserNotifications Framework

When building against iOS 8+ and not using the UserNotifications framework, use the following:

if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) {
  UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:nil];
  [[UIApplication sharedApplication] registerForRemoteNotifications];
  [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
let types : UIUserNotificationType = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
var setting : UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil)
UIApplication.shared.registerUserNotificationSettings(setting)
UIApplication.shared.registerForRemoteNotifications()

Step 5: Register Push Tokens With Appboy

Once APNs registration is complete, the following method must be altered to pass the resulting deviceToken to Appboy so the user becomes enabled for push notifications:

Add the following code to your application:didRegisterForRemoteNotificationsWithDeviceToken: method:

[[Appboy sharedInstance] registerPushToken:
                [NSString stringWithFormat:@"%@", deviceToken]];

Add the following code to your app’s application(_:didRegisterForRemoteNotificationsWithDeviceToken:) method:

let deviceTokenString = String(format: "%@", deviceToken as CVarArg)
Appboy.sharedInstance().registerPushToken(deviceTokenString)

Note: The application:didRegisterForRemoteNotificationsWithDeviceToken: delegate method is called every time after [[UIApplication sharedApplication] registerForRemoteNotifications] is called. If you are migrating to Appboy from another push service and your user’s device has already registered with APNs, this method will collect tokens from existing registrations the next time the method is called, and users will not have to re-opt-in to push.

Step 6: Enable Push Handling

The following code passes received push notifications along to Appboy and is necessary for logging push analytics and link handling.

iOS 10+

When building against iOS 10+ we recommend you integrate the UserNotifications framework and do the following:

Add the following code to your application’s application:didReceiveRemoteNotification:fetchCompletionHandler: method:

[[Appboy sharedInstance] registerApplication:application
                didReceiveRemoteNotification:userInfo
                      fetchCompletionHandler:completionHandler];

Next, add the following code to your app’s userNotificationCenter:didReceiveRemoteNotification:withCompletionHandler: method:

[[Appboy sharedInstance] userNotificationCenter:center
                 didReceiveNotificationResponse:response
                          withCompletionHandler:completionHandler];

Foreground Push Handling

In iOS 10, you can display a push notification while the app is in the foreground by implementing the following delegate method and returning UNNotificationPresentationOptionAlert to the completionHandler:

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler

In this case, if the user clicks the displayed foreground push, the new iOS 10 push delegate method userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: will be called and Appboy will log a click for that push.

Add the following code to your app’s application(_:didReceiveRemoteNotification:fetchCompletionHandler:) method:

Appboy.sharedInstance().registerApplication(application,
                                            didReceiveRemoteNotification: userInfo,
                                            fetchCompletionHandler: completionHandler)

Next, add the following code to your app’s userNotificationCenter(_:didReceive:withCompletionHandler:) method:

Appboy.sharedInstance().userNotificationCenter(center,
                                               didReceiveNotificationResponse: response,
                                               withCompletionHandler: completionHandler)

Foreground Push Handling

In iOS 10, you can display a push notification while the app is in the foreground by implementing the following delegate method and returning UNNotificationPresentationOptionAlert to the completionHandler:

func userNotificationCenter(_ center: UNUserNotificationCenter,
                willPresent notification: UNNotification,
      withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)

In this case, if the user clicks the displayed foreground push, the new iOS 10 push delegate method userNotificationCenter(_:didReceive:withCompletionHandler:) will be called and Appboy will log a click for that push.

Pre-iOS 10

Note: iOS 10 updated behavior such that it no longer calls application:didReceiveRemoteNotification:fetchCompletionHandler: when a push is clicked. For this reason, if you don’t update to building against iOS 10+ and use the UserNotifications framework, you have to call Appboy from both old-style delegates, which is a break from our previous integration.

For apps building against SDKs < iOS 10, use the following instructions:

To enable open tracking on push notifications, add the following code to your app’s application:didReceiveRemoteNotification:fetchCompletionHandler: method:

[[Appboy sharedInstance] registerApplication:application
                didReceiveRemoteNotification:userInfo
                      fetchCompletionHandler:completionHandler];

To support push analytics on iOS 10, you must also add the following code to your app’s application:didReceiveRemoteNotification: delegate method:

[[Appboy sharedInstance] registerApplication:application
                didReceiveRemoteNotification:userInfo];

To enable open tracking on push notifications, add the following code to your app’s application(_:didReceiveRemoteNotification:fetchCompletionHandler:) method:

Appboy.sharedInstance().registerApplication(application,
  didReceiveRemoteNotification: userInfo,
  fetchCompletionHandler: completionHandler)

To support push analytics on iOS 10, you must also add the following code to your app’s application(_:didReceiveRemoteNotification:) delegate method:

Appboy.sharedInstance().registerApplication(application,
  didReceiveRemoteNotification: userInfo)

Step 7: Verify Code Modifications

Verify the code modifications you made against this sample AppDelegate.m file. We also strongly advise looking through the below section on customization to determine if any additional changes need to be implemented.

Step 8: Deep Linking

Deep linking from a push into the app is automatically handled via our standard push integration documentation. If you’d like to learn more about how to add deep links to specific locations in your app, see our Advanced Use Cases section on Deep Linking for iOS.

iOS 10 Rich Notifications

iOS 10 introduces the ability to send push notifications with images, gifs, and video. To enable this functionality, clients must create a Service Extension, a new type of extension that enables modification of a push payload before it is displayed.

Creating A Service Extension

To create a Notification Service Extension, navigate to File > New > Target and select Notification Service Extension.

Adding a Service Extension

Ensure that Embed In Application is set to embed the extension in your application.

Setting Up The Service Extension

A Notification Service Extension is its own binary that is bundled with your app. As such, it must be set up in the Apple Developer Portal with its own App ID and Provisioning Profile. Typically extensions are named with a suffix on the main application’s ID (e.g., com.appboy.stopwatch.stopwatchnotificationservice).

Configuring The Service Extension To Work With Appboy

Appboy sends down an attachment payload in the APNS payload under the ab key that we use to configure, download and display rich content:

For example:

{
  "ab" :
    {
    ...

    "att" :  
      {
       "url" : "http://mysite.com/myimage.jpg",
       "type" : "jpg"
       }
    },
  "aps" :
    {
    ...
    }
}

The relevant payload values are:

// The Appboy dictionary key
static NSString *const AppboyAPNSDictionaryKey = @"ab";

// The attachment dictionary
static NSString *const AppboyAPNSDictionaryAttachmentKey = @"att";

// The attachment URL
static NSString *const AppboyAPNSDictionaryAttachmentURLKey = @"url";

// The type of the attachment - a suffix for the file you save
static NSString *const AppboyAPNSDictionaryAttachmentTypeKey = @"type";

To manually display push with an Appboy payload, download the content from the value under AppboyAPNSDictionaryAttachmentURLKey, save it as a file with the file type stored under the AppboyAPNSDictionaryAttachmentTypeKey key, and add it to the notification attachments.

We provide sample code that you can copy into your Notification Service Extension. Simply changing the class name to the one you picked will automatically provide this functionality.

You can write the Service Extension in either Objective-C or Swift. If you don’t wish to modify our default behavior, we’d recommend using our provided sample code, which is written in Objective-C. If you want to use Swift in your Service Extension, you should create a separate file with methods for our sample code, then create a bridging header file to call those methods.

For our sample code, see the Stopwatch sample application. For Swift sample code, see the Hello Swift sample application.

Creating A Rich Notification In Your Dashboard

To create a rich notification in your Appboy dashboard, simple create an iOS push and attach an image or gif, or provide a url that hosts an image, gif, or video. Note that assets are downloaded on the receipt of push notifications, so that if you are hosting your own content you should plan for large, synchronous spikes in requests.

Also note the supported file types and sizes, listed here.

Adding Rich Content

Action Buttons

iOS 8+ introduces the ability for users to interact with your application via notification categories. Categories define a type of notification your application can send. Each category contain actions that a user can perform in response, which manifest as buttons on the push notification.

Illustration of Notification Action

iOS SDK version 2.27.0 introduced default Appboy push categories, including URL handling support for each push action button. Currently, the default categories have four sets of push action buttons: Accept/Decline, Yes/No, Confirm/Cancel and More. To register Appboy’s default push categories, follow the integration instructions below:

Step 1: Adding Appboy Default Push Categories

Use the following code to register for Appboy’s default push categories when you register for push:

// For UserNotification.framework
NSSet *appboyCategories = [ABKPushUtils getAppboyUNNotificationCategorySet];
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:appboyCategories];

// For UIUserNotificationSettings
NSSet *appboyCategories = [ABKPushUtils getAppboyUIUserNotificationCategorySet];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge
                                                                         categories:appboyCategories];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
// For UserNotification.framework
let appboyCategories = ABKPushUtils.getAppboyUNNotificationCategorySet()
UNUserNotificationCenter.current().setNotificationCategories(appboyCategories)

// For UIUserNotificationSettings
let appboyCategories = ABKPushUtils.getAppboyUIUserNotificationCategorySet()
let settings = UIUserNotificationSettings.init(types: .badge, categories: appboyCategories)
UIApplication.shared.registerUserNotificationSettings(settings)

Note: Clicking on push action buttons with background activation mode will only dismiss the notification and will not open the app. Button click analytics for these actions will be flushed to the server the next time the user opens the app.

Note: If you wish to create your own custom notification categories, see our action button customization documentation.

See our sample code here for UserNotification.framework and here for UIUserNotificationSettings.

Step 2: Enable Interactive Push Handling

To enable Appboy’s push action button handling, including click analytics and URL routing, add the following code to your app’s application:handleActionWithIdentifier:forRemoteNotification:completionHandler: delegate method:

[[Appboy sharedInstance] getActionWithIdentifier:identifier
                           forRemoteNotification:userInfo
                               completionHandler:completionHandler];
Appboy.sharedInstance().getActionWithIdentifier(identifier,
                         forRemoteNotification: userInfo,
                             completionHandler: completionHandler)

Customization

Badge Counts

Note: If badge counts are enabled, Appboy will display a badge when a customer has unread notifications. By default, this number is 1; the badge count must be manually set through your application’s applicationIconBadgeNumber property or through the remote notification payload. Appboy will only clear the badge count when an Appboy remote notification is received by registerApplication:didReceiveRemoteNotification: or registerApplication:didReceiveRemoteNotification:fetchCompletionHandler: while the app is foregrounded. This is to avoid interfering with any other badges stemming from other notifications within the app.

If you do not have or do not plan to have other ways of altering your app’s badge count, you should add the following code to your app’s applicationDidBecomeActive: delegate method:

[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
UIApplication.shared.applicationIconBadgeNumber = 0

Please note that setting the badge number to 0 will also clear up notifications in the notification center. So even if you don’t set badge number in push payloads, you can still set the badge number to 0 to remove the push notification(s) in the notification center after users clicked on the push.

Action Buttons

In addition to providing a set of default push categories, Appboy supports custom notification categories and actions. Once you register categories in your application, you can use the Appboy dashboard to send notification categories to your users.

For an example of setting up custom actions and categories, please see the setupRemoteNotificationForiOS10 or setupRemoteNotificationForiOS8And9 methods within the AppDelegate.m file of Stopwatch. Note: If you haven’t migrated to iOS 10, please see this alternative categories documentation.

These categories can then be assigned to push notifications via our dashboard to trigger the action button configurations of your design. Here’s an example which leverages the “LIKE_CATEGORY” from our Stopwatch sample app:

Dashboard Category Send

Here’s what that looks like on the device!

Push Example with Buttons

Sample Appboy Apple Push Payload

When Appboy sends a push notification, the payload will look like this. You should avoid handling a top-level dictionary called ab or aps in your application:

{
  aps: {
    alert: {
     body: "your push message",
     title: "your message title"
    },
    badge: 1,
    ...
  },
  ab: {
    c: "your campaign id",
    att: { // optional, required for campaigns with iOS 10 rich notifications
      url: (optional, string) attachment url,
      type: (optional, string) attachment filetype
    },
    ab_cat: { // optional, required for campaigns with push action buttons
      <action_id>: (required) {
        a_t: (required, string) click action type,
        a_uri: (optional, string) uri to open when the push action button is clicked,
        a_use_webview: (optional, boolean) open the a_uri in a UIWebView if true
      },
      ... Other Buttons ...
    },
   }
  ab_uri: (optional, string) uri to open when push notification is clicked,
  ab_use_webview: (optional, boolean) open the ab_uri in a UIWebView if true,
  <custom_key>: "foo",
  ... Custom Key/Value Pairs ...
}

Note: the alert can also just be a string, which will be the push message text.

In the case that your push notification does not have a campaign ID, the key c will have the value NSNull. This value is necessary and should not be sanitized or removed. Some cases where there is no campaign ID are sending a push from an API campaign, a test push from the dashboard, or a test curl.

Custom Sounds and Push

Step 1: Hosting the Sound in the App

Custom push notification sounds must be hosted locally within the main bundle of the client application. The following audio data formats are accepted:

  • Linear PCM
  • MA4
  • µLaw
  • aLaw

You can package the audio data in an aiff, wav, or caf file. Then, in Xcode, add the sound file to your project as a nonlocalized resource of the application bundle.

You may use the afconvert tool to convert sounds. For example, to convert the 16-bit linear PCM system sound Submarine.aiff to IMA4 audio in a CAF file, use the following command in the Terminal application:

afconvert /System/Library/Sounds/Submarine.aiff ~/Desktop/sub.caf -d ima4 -f caff -v

You can inspect a sound to determine its data format by opening it in QuickTime Player and choosing Show Movie Inspector from the Movie menu.

Custom sounds must be under 30 seconds when played. If a custom sound is over that limit, the default system sound is played instead.

Step 2: Providing the Dashboard with a Protocol URL for the Sound

Your sound must be hosted locally within the app. You must specify a Protocol URL which directs to the location of the sound file in the app within the “Sound” field in the push composer. Specifying “default” in this field will play the default notification sound on the device. This can be specified via our Messaging API or our dashboard under “Advanced Settings” in the push composer wizard as pictured below:

Push Notification Sound

If the specified sound file doesn’t exist or the keyword ‘default’ is entered, Appboy will use the default device alert sound. Aside from our dashboard, sound can also be configured via our our Messaging API. For additional information see the Apple Developer Documentation regarding “Preparing Custom Alert Sounds”.

Ignoring Appboy’s Internal Push Notifications

Appboy uses silent push notifications for internal implementation of certain advanced features. For most integrations, this requires no changes on your app’s behalf. However, if you integrate an Appboy feature that relies on internal push notifications (i.e., uninstall tracking or geofences), you may want to update your app to ignore Appboy’s internal pushes.

If your app takes automatic actions on application launches or background pushes, you should consider gating that activity so that it’s not triggered by Appboy’s internal push notifications. For example, if you have logic that calls your servers for new content upon every background push or application launch, you likely would not want Appboy’s internal pushes triggering that because you would incur unnecessary network traffic. Furthermore, because Appboy sends certain kinds of internal pushes to all users at approximately the same time, not gating network calls on launch from internal pushes could introduce significant server load.

Checking Your App for Automatic Actions

You should check your application for automatic actions in the following places and update your code to ignore Appboy’s internal pushes:

  1. Push Receivers. Background push notifications will call application:didReceiveRemoteNotification:fetchCompletionHandler: on the UIApplicationDelegate.
  2. Application Delegate. Background pushes can launch suspended apps into the background, triggering the application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: methods on your UIApplicationDelegate. You can check the launchOptions of these methods to determine if the application has been launched from a background push.

Using Appboy’s Internal Push Utility Methods

You can use the utility methods in ABKPushUtils to check if your app has received or was launched by an Appboy internal push. isAppboyInternalRemoteNotification: will return YES on all Appboy internal push notifications, while isUninstallTrackingRemoteNotification: and isGeofencesSyncRemoteNotification: will return YES for uninstall tracking and geofences sync notifications, respectively.

See ABKPushUtils.h for method declarations and our Stopwatch sample application for example implementations.

Implementation Example

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  NSDictionary *pushDictionary = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
  BOOL launchedFromAppboyInternalPush = pushDictionary && [ABKPushUtils isAppboyInternalRemoteNotification:pushDictionary];
  if (!launchedFromAppboyInternalPush) {
    // ... Gated logic here (e.g., pinging your server to download content) ...
  }
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
  if (![ABKPushUtils isAppboyInternalRemoteNotification:userInfo]) {
    // ... Gated logic here (e.g., pinging server for content) ...
  }
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
  let pushDictionary = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary as? [AnyHashable : Any] ?? [:]
  let launchedFromAppboyInternalPush = ABKPushUtils.isAppboyInternalRemoteNotification(pushDictionary)
  if (!launchedFromAppboyInternalPush) {
    // ... Gated logic here (e.g., pinging your server to download content) ...
  }
}
func application(_ application: UIApplication,
                 didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  if (!ABKPushUtils.isAppboyInternalRemoteNotification(userInfo)) {
    // ... Gated logic here (e.g., pinging server for content) ...
  }
}

Extracting Data from Push Notification Key/Value Pairs

Appboy allows you to send additional key/value pairs along with a push notification to be used however you so please via your application within the extras property. Please see the following example code for extracting data from that key/value pair:

- (void)handleExtrasFromPush:(NSDictionary *)notification {
  NSLog(@"A push was received");
  if (notification !=nil && [notification objectForKey:@"Some_Key"] !=[NSNull null] && [[notification objectForKey:@"Some_Key"] length] > 0 ) {  
    NSString *value = [NSString stringWithFormat:@"%@",[notification objectForKey:@"Some_Key"]];  
    // Here based on the extras key-value pair, you can run some arbitrary code.
  }
}

Testing

If you’d like to test in-app and push notifications via the command-line, you can send a single notification through the terminal via CURL and the Messaging API. You will need to replace the following fields with the correct values for your test case:

curl -X POST -H "Content-Type: application/json" -d "{\"app_group_id\":\"YOUR_APP_GROUP_ID\",\"external_user_ids\":[\"YOUR_EXTERNAL_USER_ID\"],\"messages\":{\"apple_push\":{\"alert\":\"Test push\",\"extra\":{\"YOUR_KEY1\":\"YOUR_VALUE1\"}}}}" https://api.appboy.com/messages/send

Troubleshooting

Understanding the Appboy/APNs workflow

The Apple Push Notification service (APNs) is Apple’s infrastructure for push notifications sending to iOS and OS X applications. Here is the simplified structure of how push notifications are enabled for your users’ devices and how Appboy is able to send push notifications to them:

Step 1: Configuring the push certificate and provisioning profile

In the development of your app, you’ll need to create an SSL certificate to enable push notifications. This certificate will be included in the provisioning profile your app is built with and will also need to be uploaded to the Appboy dashboard. The certificate allows Appboy to tell APNs that we are allowed to send push notifications on your behalf.

There are two types of provisioning profiles and certificates - development and distribution. We recommend just using distribution profiles/certificates to avoid any confusion. If you choose to use different profiles and certificates for development and distribution, make sure that the certificate uploaded to the dashboard matches the provisioning profile you are currently using. You can read more about provisioning profiles here.

Step 2: Devices register for APNs and provide Appboy with push tokens

When users open your app, they will be prompted to accept push notifications. If they accept this prompt, then APNs will generate a push token for that particular device. The Appboy SDK will send this token along when the current session is closed. After we have a push token associated with a user, they will show as “Push Registered” in the dashboard on their user profile under the “Engagement” tab and will be eligible to receive push notifications from Appboy campaigns. Note: This does not work with the iOS Simulator. You cannot test push notifications with the iOS Simulator as a result.

Step 3: Launching an Appboy push campaign

When a push campaign is launched, Appboy will make requests to APNs to deliver your message. Appboy will use the SSL push certificate uploaded in the dashboard to authenticate and verify that we are allowed to send push notifications to the push tokens provided. If a device is online, the notification should be received shortly after the campaign has been sent. Appboy sets the default APNs expiration date for notifications to 30 days.

Step 4: Removing invalid tokens

If APNs informs us that any of the push tokens we were attempting to send a message to are invalid, we remove those tokens from the user profiles they were associated with.

Apple has more details about APNs in their Developer Library.

Utilizing the Push Error Logs

Appboy provides a log of Push Notification Errors within the Message Activity Log. This error log provides a variety of warnings which can be very helpful for identifying why your campaigns aren’t working as expected. Clicking on an error message will redirect you to relevant documentation to help you troubleshoot a particular incident.

Push Error Log

Common errors you might see here include user-specific notifications, such as “Received Unregistered Sending to Push Token”.

In addition, Appboy also provides a Push Changelog on the user profile under the Engagement tab. This changelog provides insight into push registration behavior such as token invalidation, push registration errors, tokens being moved to new users, etc.

Push Changelog

Push Registration Issues

No Push Registration Prompt

If the application does not prompt you to register for push notifications, there is likely an issue with your push registration integration. Make sure you have followed our documentation and correctly integrated our push registration. You can also set breakpoints in your code to ensure push registration code is running.

No “Push Registered” Users Showing in the Dashboard

  • Ensure that your app is prompting you to allow push notifications. Typically this prompt will appear upon your first open of the app, but it can be programmed to appear elsewhere. If it is not appearing where it should be, then the problem is likely with the basic configuration of your app’s push capabilities.
    • Verify the steps under “Enabling Message Channels” > “Push Notifications” were successfully completed.
    • Make sure that the provisioning profile your app was built with includes permissions for push. Make sure that you’re pulling down all of the available provisioning profiles from your Apple Developer account, as well. You can confirm this by navigating to “Preferences” > “Accounts” in Xcode (Command+,).

    Provisioning Profile Refresh Step 1

    Click on the Apple ID you use for your developer account and then “View Details…”. In the bottom left corner of the pane that opens up, click on the refresh icon.

    Provisioning Profile Refresh Step 2

  • Make sure you have properly enabled push capability in your app.
  • Make sure your push provisioning profile matches the environment you’re testing in. Appboy only supports universal certificates for production environments and testing push on your development environments with a universal certificate will not work. Similarly, using a development certificate in production will not work.
  • Ensure that you are calling our registerPushToken method by setting a breakpoint in your code.
  • Ensure that you are on a device (push will not work on a simulator) and have good network connectivity.

Devices Not Receiving Push Notifications

Users No Longer “Push Registered” After Sending a Push Notification

This would likely indicate that user had an invalid push token. This can happen for several reasons:

Dashboard/App Certificate Mismatch

If the push certificate that you uploaded in the dashboard is not the same one in the provisioning profile that your app was built with, APNs will reject the token. Verify that you have uploaded the correct certificate and complete another session in the app before attempting another test notification.

Uninstalls

If a user has uninstalled your application, then their push token will be invalid and removed upon the next send.

Regenerating Your Provisioning Profile

As a last resort, starting over fresh and creating a whole new provisioning profile can clear up configuration errors that come from working with multiple environments, profiles and apps at the same time. There are many “moving parts” in setting up push notifications for iOS apps, so sometimes it is best to retry from the beginning. This will also help isolate the problem if you need to continue troubleshooting.

Users Still “Push Registered” After Sending a Push Notification

App Is Foregrounded

On iOS versions that do not integrate push via the UserNotifications framework, if the app is in the foreground when the push message is received it will not be displayed. You should background the app on your test devices before sending test messages.

Test Notification Scheduled Incorrectly

Check the schedule you set for your test message. If it is set to local time zone delivery or Intelligent Delivery, you may have just not received the message yet (or had the app in the foreground when it was received).

User Not “Push Registered” For the App Being Tested

Check the user profile of the user you are trying to send a test message to. Under the “Engagement” tab, there should be a list of “Pushable Apps”. Verify the app you are trying to send test messages to is in this list. Users will show up as “Push Registered” if they have a push token for any app in your app group, so this could be something of a false positive.

The following would indicate a problem with push registration, or that the user’s token had been returned to Appboy as invalid by APNs after being pushed:

Push Problem

Message Activity Log Errors

Received Unregistered Sending to Push Token

  • Make sure that the push token being sent to Appboy from the method [[Appboy sharedInstance] registerPushToken:] is valid. You can look in the Message Activity Log to see the push token. It should look something like 6e407a9be8d07f0cdeb9e724733a89445f57a89ec890d63867c482a483506fa6, a string about that length with a mix of letters and numbers. If your push token looks different, check your code for sending Appboy the push tokens.
  • Ensure that your push provisioning profile matches the environment you’re testing in. Appboy only supports universal certificates for production environments and testing push on your development environments with a universal certificate will not work. Similarly, using a development certificate in production will not work.
  • Check that the push token you have uploaded to Appboy matches the provisioning profile you used to build the app you sent the push token from.

Issues After Push Delivery

Push Clicks Not Logged

  • If this is only occurring on iOS 10, make sure you have followed the push integration steps for iOS 10.
  • Appboy does not handle push notifications received silently in the foreground (e.g., default foreground push behavior prior to the UserNotifications framework). This means that links will not be opened and push clicks will not be logged. If your application has not yet integrated the UserNotifications framework, Appboy will not handle push notifications when the application state is UIApplicationStateActive. You should ensure that your app is not delaying calls to Appboy’s push handling methods, otherwise the iOS SDK may be treating push notifications as silent foreground push events and not handing them.

iOS 9+ requires links be ATS compliant in order to be opened in web views. Ensure that your web links use HTTPS. For more information, refer to our documentation on ATS compliance.

Most of the code that handles deep links also handles push opens. First, ensure that push opens are being logged; if not, first fix that issue (as the fix often fixes link handling).

If opens are being logged, check to see if it is an issue with the deep link in general or with the deep linking push click handling. To do this, test to see if a deep link from an In-App Message click works.

If deep links are working normally and opens are working, but deep links don’t work from our push clicks, check what version of the SDK you are on. In 2.21.0 we required deep links to be whitelisted in order to function properly. We removed this in 2.24.2. If the issue is happening on a version in between, ensure that the deep link is whitelisted.

Silent Push Notifications

Remote notifications allow you to notify your app when important events occur. You might have new instant messages to deliver, breaking news alerts to send, or the latest episode of your user’s favorite TV show ready for him or her to download for offline viewing. Remote notifications are great for sporadic but immediately important content, where the delay between background fetches might not be acceptable. Remote Notifications can also be much more efficient than Background Fetch, as your application only launches when necessary.

A Remote Notification is really just a normal Push Notification with the content-available flag set. You might send a push with an alert message informing the user that something has happened, while you update the UI in the background. But Remote Notifications can also be silent, containing no alert message or sound, used only to update your app’s interface or trigger background work. You might then post a local notification when you’ve finished downloading or processing the new content.

Silent push notifications are rate-limited, so don’t be afraid of sending as many as your application needs. iOS and the APNs servers will control how often they are delivered, and you won’t get into trouble for sending too many. If your push notifications are throttled, they might be delayed until the next time the device sends a keep-alive packet or receives another notification.

Sending Remote Notifications

To send a remote notification, set the content-available flag in a push notification payload. When you’re sending a Remote Notification, you might also want to include some data in the notification payload, so your application can reference the event. This could save you a few networking requests and increase the responsiveness of your app.

The content-available flag can be set in the Appboy dashboard (pictured below) as well as within our User API.

content-available

Use Silent Remote Notifications to Trigger Background Work

Silent remote notifications can wake your app from a “Suspended” or “Not Running” state, to update content or run certain tasks without notifying your users. To send a silent remote push notification, you just need to set up the content-available flag with no message nor sound. Please set up your app’s background mode to enable remote notifications under the “Capabilities” tab in your project settings.

background-mode-enabled

Note: Enabling background mode for remote notifications is required for Appboy’s Uninstall Tracking feature.

Note: Even with the remote notifications background mode enabled, the system will not launch your app into the background if the user has force-quit the application. The user must explicitly launch the application or reboot the device before the app can be automatically launched into the background by the system. For more information, please refer to Apple’s documentation on Background Execution and application:didReceiveRemoteNotification:fetchCompletionHandler:.

In-App Messaging

In-app messages are great for creating unobtrusive calls to action, notifying people of new content in the news feed and driving them toward it or communicating with users who have push turned off. In-app messages are also effective for other content that isn’t time-sensitive enough to warrant a push notification, or permanent enough to warrant a news feed item. You can find a detailed explanation of in-app message behavior in Appboy Academy.

By default, in-app messages are enabled after completing the standard SDK integration, including GIF support. Note that if you did not integrate SDWebImage, in-app messages with images will not work.

In-App Message Types

Appboy currently offers the following default in-app message types: Slideup, Modal, Full and HTML Full. Each in-app message type is highly customizable across content, images, icons, click actions, analytics, display and delivery.

All in-app messages are subclasses of the ABKInAppMessage, which defines basic behavior and traits for all in-app messages. The in-app message class structures as following:

ABKInAppMessage models

Slideup In-App Messages

Slideup in-app messages are so-named because they “slide up” or “slide down” from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability.

Slideup Example

Modal in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two click action and analytics enabled buttons.

Modal Example

Full In-App Messages

Full in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a full in-app message contains an image and the lower half displays text as well as up to two click action and analytics enabled buttons.

Full Example

HTML Full In-App Messages

HTML Full in-app messages are useful for creating fully customized user content. User-defined HTML Full in-app message content is displayed in a UIWebView and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality.

The following example shows a paginated HTML Full in-app message: HTML5 Example

In-App Message Delivery

In-App Messages (Triggered)

The following documentation refers to Appboy’s In-App Messaging product, aka “triggered in-app messages,” which are branded as highlighted below in the “Create Campaign” drop-down:

In-App Messaging Composer

You may also refer to the documentation for our deprecated Original In-App Messaging product.

Trigger Types

Our in-app message product allows you to trigger in-app message display as a result of several different event types: Any Purchase, Specific Purchase, Session Start, Custom Event, Push Click. Furthermore, Specific Purchase and Custom Event triggers can contain robust property filters.

-Note: Triggered in-app messages only work with custom events logged through the SDK and not through the Rest APIs. If you’re working with Android, please check out how to log custom events here. If you’re working with iOS, check out how to log custom events here.

Delivery Semantics

All in-app messages that a user is eligible for are delivered to the user’s device on session start. For more information about the SDK’s session start semantics, see our session lifecycle documentation. Upon delivery, the SDK will pre-fetch assets so that they are available immediately at trigger time, minimizing display latency.

When a trigger event has more than one eligible in-app message associated with it, only the in-app message with the highest priority will be delivered.

For in-app messages that display immediately on deliver (i.e., session start, push click) there can be some latency due to assets not being prefetched.

Minimum Time Interval Between Triggers

By default, we rate limit in-app messages to once every 30 seconds to ensure a quality user experience.

You can override this value via the ABKMinimumTriggerTimeIntervalKey inside the appboyOptions parameter passed to startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions:. Set the ABKMinimumTriggerTimeIntervalKey to the integer value you want as your minimum time in seconds between in-app messages:

// Sets the minimum trigger time interval to 5 seconds
[Appboy startWithApiKey:@"YOUR-API_KEY"
          inApplication:application
      withLaunchOptions:options
      withAppboyOptions:@{ ABKMinimumTriggerTimeIntervalKey : @(5) }];

An example of overriding the default trigger interval can be found in our sample application’s AppDelegate.m file.

Original In-App Messages (Deprecated)

The following documentation refers to Appboy’s Original In-App Messaging product, branded as highlighted below in the “Create Campaign” drop-down:

Original In-App Messaging Composer

Original in-app messages request one in-app message at a time on session start. They are a deprecated feature and will be eventually removed in favor of triggered in-app messages. See our documentation about the key differences between original and triggered in-app messages, including reasons to use the latter.

Requesting New Original In-App Messages

As of SDK v2.7, you can manually request a new original in-app message for the user in Appboy.h using requestInAppMessageRefresh:. For example:

[[Appboy sharedInstance] requestInAppMessageRefresh];
Appboy.sharedInstance().requestInAppMessageRefresh()

Circumventing Appboy In-App Message Handling

Note: This delegate is NOT supported for triggered in-app messages.

The following delegate method is called every time new in-app message is received from the Appboy server.

Advanced Users Only: To implement completely custom handling of the Appboy in-app message, use the following code and return YES to circumvent Appboy’s default in-app message handling:

- (BOOL) onInAppMessageReceived:(ABKInAppMessage *)inAppMessage;{
  //Return NO when you want Appboy to handle the display of the in-app message.
  return YES;
}
func onInAppMessageReceived(inAppMessage: ABKInAppMessage!) -> Bool {
  //Return false when you want Appboy to handle the display of the in-app message.
  return true;
}

Example Template: Custom AppStore Review Prompt

Creating a campaign to ask users for an App Store review is a popular usage of in-app messages.

Start by setting the In-App Message delegate in your app and implement the following delegate to disable the default app store review message:

(ABKInAppMessageDisplayChoice)beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage withKeyboardIsUp:(BOOL)keyboardIsUp {
   if (inAppMessage.extras != nil && inAppMessage.extras[@"Appstore Review"] != nil) {
     [[UIApplication sharedApplication] openURL:inAppMessage.uri];
     return ABKDiscardInAppMessage;
   } else {
     Return ABKDisplayInAppMessageNow;
   }
}

In your deep link handling code, you can then add the following code to process the {your_app_scheme}:appstore-review deep link:

(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  NSString *urlString = url.absoluteString.stringByRemovingPercentEncoding;
  if ([urlString isEqualToString:@"{your_app_scheme}:appstore-review"]) {
    [SKStoreReviewController requestReview];
    return YES;
  }
  // Other deep link handling code…
}

Next, create a campaign and add the key value pair “Appstore Review” : “true” and a deep link to the delegate method just created, e.g. {your_app_scheme}:appstore-review.

Apple will only allow you to ask each to review your app up to 3 times a year, so the campaign should be rate-limited to three times per year per user.

Local In-App Message Delivery

The In-App Message Stack

Showing In-App Messages

When a user is eligible to receive an in-app message, the ABKInAppMessageController will be offered the latest in-app message off the in-app message stack. The stack only persists stored in-app messages in memory and is cleared up between app launches from suspended mode.

Adding In-App Messages to the Stack

Users are eligible to receive an in-app message in the following situations:

Triggered in-app messages are placed on top of the stack when their trigger event is fired. If multiple in-app messages are in the stack and waiting to be displayed, Appboy will display the most recently received in-app message first (last in, first out).

Returning In-App Messages to the Stack

A triggered in-app message can be returned back to the stack in the following situations:

  • The in-app message is triggered when the app is in the background
  • Another in-app message is currently visible
  • The beforeInAppMessageDisplayed:withKeyboardIsUp: delegate method has NOT been implemented, and the keyboard is currently being displayed
  • The beforeInAppMessageDisplayed:withKeyboardIsUp: delegate method returned ABKDisplayInAppMessageLater
Discarding In-App Messages

A triggered in-app message will be discarded in the following situations:

  • The beforeInAppMessageDisplayed:withKeyboardIsUp: delegate method returned ABKDiscardInAppMessage
  • The asset (image or zip file) of the in-app message failed to download
  • The in-app message is ready to be displayed but past the timeout duration
  • The device orientation doesn’t match the triggered in-app message’s orientation
  • The in-app message is a full in-app message but has no image
  • The in-app message is a image-only modal in-app message but has no image

Manually Queue In-App Message Display

If you wish to display an in-app message at other times within your app, you may manually offer the top-most in-app message on the stack to your in-app message delegate object by calling the following method:

[[Appboy sharedInstance].inAppMessageController displayNextInAppMessageWithDelegate:YOUR_IN_APP_MESSAGE_DELEGATE]
// YOUR_IN_APP_MESSAGE_DELEGATE should be replaced with your in-app message delegate.

Real Time In-App Message Creation & Display

In-app messages can also be locally created within the app and displayed via Appboy. This is particularly useful for displaying messages that you wish to trigger within the app in real-time. Appboy does not support analytics on in-app messages created locally.

- (IBAction)createAndDisplayACustomInAppMessage:(id)sender {
  ABKInAppMessageSlideup *customInAppMessage = [[ABKInAppMessageSlideup alloc] init];
  customInAppMessage.message = @"YOUR_CUSTOM_SLIDEUP_MESSAGE";
  customInAppMessage.duration = 2.5;
  customInAppMessage.extras = @{@"key" : @"value"};
  [[Appboy sharedInstance].inAppMessageController addInAppMessage:customInAppMessage];
}

Customization

All of Appboy’s in-app message types are highly customizable across messages, images, Font Awesome icons, click actions, analytics, editable styling, custom display options, and custom delivery options. Multiple options can be configured on a per in-app message basis from within the dashboard. Appboy additionally provides multiple levels of advanced customization to satisfy a variety of use cases and needs.

Foodo In-App Message Customization Example

An example of in-app message customization from an Appboy client.

Key-Value Pair Extras

ABKInAppMessage objects may carry key-value pairs as extras. These are specified on the dashboard when creating an in-app message campaign. Key-value pairs can be used to send data down along with an in-app message for further handling by your app, allowing you to add custom behaviors on top of what Appboy provides.

Setting a Delegate

In-app message display and delivery customizations can be accomplished in code by setting a delegate. All of these customizations are entirely optional.

Set the delegate on ABKInAppMessageController by calling:

[Appboy sharedInstance].inAppMessageController.delegate = self;
Appboy.sharedInstance().inAppMessageController.delegate = self

In some cases (e.g., on session start), the in-app message may be triggered and displayed before the in-app message delegate is set. To guard against this, you can also set the in-app message delegate in the appboyOptions with key ABKInAppMessageControllerDelegateKey in startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions.

[Appboy startWithApiKey:@"YOUR-API_KEY"
          inApplication:application
      withLaunchOptions:options
      withAppboyOptions:@{ ABKInAppMessageControllerDelegateKey : self }];
Appboy.startWithApiKey("YOUR-API-KEY",
                       inApplication:application,
                       withLaunchOptions:launchOptions,
                       withAppboyOptions:[ ABKInAppMessageControllerDelegateKey : self ]])

Customizing Fixed Orientation

Setting Orientation For All In-App Messages

To set a fixed orientation for all in-app messages, you can set the supportedOrientationMasks property on ABKInAppMessageController. Add the following code after your app’s call to startWithApiKey:inApplication:withLaunchOptions::

// Set fixed in-app message orientation to portrait.
[Appboy sharedInstance].inAppMessageController.supportedOrientationMasks = UIInterfaceOrientationMaskPortrait;
// Use UIInterfaceOrientationMaskLandscape to display in-app messages in landscape
// Set fixed in-app message orientation to portrait
Appboy.sharedInstance().inAppMessageController.supportedOrientationMasks = portrait;
// Use landscape to display in-app messages in landscape

Following this, all in-app messages will be displayed in the supported orientation, regardless of device orientation. Please note that the device orientation must also be supported by the in-app message’s orientation property in order for the message to display. For more information, see the section below.

Setting Orientation Per In-App Message

Alternatively, you can set orientation on a per-in-app message basis. To do this, set an in-app message delegate. Then, in the beforeInAppMessageDisplayed:withKeyboardIsUp: delegate method, set the orientation property on the ABKInAppMessage. As an example:

// Set inAppMessage orientation to portrait
inAppMessage.orientation = ABKInAppMessageOrientationPortrait;

// Set inAppMessage orientation to landscape
inAppMessage.orientation = ABKInAppMessageOrientationLandscape;

In-app messages will not display if the device orientation does not match the orientation property on the in-app message.

Customizing In-App Message Display Behavior

The following delegate method is called each time right before an in-app message is displayed:

- (ABKInAppMessageDisplayChoice) beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage withKeyboardIsUp:(BOOL)keyboardIsUp;
func beforeInAppMessageDisplayed(inAppMessage: ABKInAppMessage!, withKeyboardIsUp keyboardIsUp: Bool) -> ABKInAppMessageDisplayChoice

You can customize in-app message handling by implementing this delegate method and returning one of the following values for ABKInAppMessageDisplayChoice:

ABKInAppMessageDisplayChoice Behavior
ABKDisplayInAppMessageNow The message will be displayed immediately
ABKDisplayInAppMessageLater The message will be not be displayed and will be placed back on the top of the stack
ABKDiscardInAppMessage The message will be discarded and will not be displayed

You can use the beforeInAppMessageDisplayed:withKeyboardIsUp: delegate method to add in-app message display logic, customize in-app messages before Appboy displays them, or opt-out of Appboy’s in-app message display logic and UI entirely.

For an implementation example, see our Stopwatch Sample Application.

Overriding In-App Messages Before Display

If you would like to alter the display behavior of in-app messages, you should add any necessary display logic to the beforeInAppMessageDisplayed:withKeyboardIsUp: delegate method. For example, you might want to display the in-app message from the top of the screen if the keyboard is currently being displayed, or take the in-app message data model and display the in-app message yourself.

Logging Impressions and Clicks

Logging in-app message impressions and clicks is not automatic when you implement completely custom handling (i.e. if you circumvent Appboy’s in-app message display by returning ABKDiscardInAppMessage in beforeInAppMessageDisplayed:withKeyboardIsUp:). If you choose to implement your own UI using our in-app message models, you must log analytics with the following methods on the ABKInAppMessage class:

// Registers that a user has viewed an in-app message with the Appboy server.
- (void) logInAppMessageImpression;
// Registers that a user has clicked on an in-app message with the Appboy server.
- (void) logInAppMessageClicked;
// Registers that a user has viewed a in-app message with the Appboy server.
func logInAppMessageImpression()
// Registers that a user has clicked on a in-app message with the Appboy server.
func logInAppMessageClicked()

Furthermore, you should be logging button clicks on subclasses of ABKInAppMessageImmersive (i.e., Modal and Full in-app messages):

/// Logs button click analytics
- (void)logInAppMessageClickedWithButtonID:(NSInteger)buttonID;
/// Logs button click analytics
func logInAppMessageClickedWithButtonID(buttonId: NSInteger)

Customizing In-App Message Behavior on Click

The inAppMessageClickActionType property on the ABKInAppMessage defines the action behavior after the in-app message is clicked. This property is read only. If you want to change the in-app message’s click behavior, you can call the following method on ABKInAppMessage:

- (void)setInAppMessageClickAction:(ABKInAppMessageClickActionType)clickActionType
                           withURI:(NSURL *)uri;

The inAppMessageClickActionType can be set to one of the following values:

ABKInAppMessageClickActionType On-Click Behavior
ABKInAppMessageDisplayNewsFeed The News Feed will be displayed when the message is clicked, and the message will be dismissed. Note: The uri parameter will be ignored, and the uri property on the ABKInAppMessage will be set to nil.
ABKInAppMessageRedirectToURI The given URI will be displayed when the message is clicked, and the message will be dismissed. Note: The uri parameter cannot be nil.
ABKInAppMessageNoneClickAction The message will be dismissed when clicked. Note: The uri parameter will be ignored, and the uri property on the ABKInAppMessage will be set to nil.

Customizing In-App Message Body Clicks

The following delegate method is called when the in-app message is clicked, and it can be used to customize in-app message on-click behavior:

- (BOOL) onInAppMessageClicked:(ABKInAppMessage *)inAppMessage;
func onInAppMessageClicked(inAppMessage: ABKInAppMessage!) -> Bool

Customizing In-App Message Button Clicks

For clicks on in-app message buttons and HTML in-app message buttons (i.e., links), you can also use the following delegate methods for customization:

- (BOOL)onInAppMessageButtonClicked:(ABKInAppMessageImmersive *)inAppMessage
                             button:(ABKInAppMessageButton *)button;

- (BOOL)onInAppMessageHTMLButtonClicked:(ABKInAppMessageHTML *)inAppMessage
                             clickedURL:(nullable NSURL *)clickedURL
                               buttonID:(NSString *)buttonID;
func onInAppMessageButtonClicked(inAppMessage: ABKInAppMessageImmersive!,
                                 button: ABKInAppMessageButton) -> Bool

func onInAppMessageHTMLButtonClicked(inAppMessage: ABKInAppMessageHTML!,
                                     clickedURL: URL, buttonID: String) -> Bool

Each method returns a BOOL value to indicate if Appboy should continue to execute the click action.

To access the click action type of a button in a delegate method, you can use the following code:

if ([inAppMessage isKindOfClass:[ABKInAppMessageImmersive class]]) {
      ABKInAppMessageImmersive *immersiveIAM = (ABKInAppMessageImmersive *)inAppMessage;
      NSArray<ABKInAppMessageButton *> *buttons = immersiveIAM.buttons;
      for (ABKInAppMessageButton *button in buttons) {
         // Button action type is accessible via button.buttonClickActionType
      }
   }
if inAppMessage is ABKInAppMessageImmersive {
      let immersiveIAM = inAppMessage as! ABKInAppMessageImmersive;
      for button in inAppMessage.buttons as! [ABKInAppMessageButton]{
        // Button action type is accessible via button.buttonClickActionType
      }
    }

Note: When an in-app message has buttons, the only click actions that will be executed are the ones on the ABKInAppMessageButton model. The in-app message body will not be clickable even though the ABKInAppMessage model will have the default click action (“News Feed”) assigned.

Display In-App Messages In a Custom View Controller

In-app messages can also be displayed within a custom view controller which you pass to Appboy. Appboy will animate the customized in-app message in and out, as well as handle analytics of the in-app message. The view controller must meet the following requirements:

  • It must be a subclass or an instance of ABKInAppMessageViewController.
  • The view of the returned view controller should be an instance of ABKInAppMessageView or its subclass.

The following delegate method is checked every time an in-app message is offered to ABKInAppMessageViewController to see if the app would like to pass a custom view controller to Appboy for in-app message display:

- (ABKInAppMessageViewController *) inAppMessageViewControllerWithInAppMessage:(ABKInAppMessage *)inAppMessage;
func inAppMessageViewControllerWithInAppMessage(inAppMessage: ABKInAppMessage!) -> ABKInAppMessageViewController!

Theming the In-App Message

Appboy’s iOS SDK supports powerful theming options based on the NUI customization framework. NUI allows you customize almost everything about the look and feel of an app using style sheets similar to CSS.

Appboy has its own specific set of UI elements which must be customized independently from the rest of your app – they do not inherit property settings from the standard NUI classes. This allows you to customize Appboy without affecting the rest of your app.

Getting Started - Theming & NUI

To theme your app, integrate NUI into your project by adding pod NUI in your Podfile, or follow these instructions.

Add this line to - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions in your AppDelegate.m file:

[NUISettings initWithStylesheet:@"Your_Theme"];
[Appboy sharedInstance].useNUITheming = YES;

First, add #import <NUI/NUISettings.h> to your bridging header file.

Next, add this line to application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool in your AppDelegate.swift file:

NUISettings.initWithStylesheet("Your_Theme")
Appboy.sharedInstance().useNUITheming = true

NUI allows you to customize the behavior of many UI elements, such as labels, buttons, views, and text fields. For each component, there is a set of properties you can set via a style sheet – NUIStyle.nss – which you include in your project.

Themable In-App Message Elements

The below class names are directly used in NUIStyle.nss. You can customize in-app message’s font by adding the following properties to the stylesheet

  • ABKInAppMessageSlideupMessageLabel
  • ABKInAppMessageeModalHeaderLabel
  • ABKInAppMessageModalMessageLabel
  • ABKInAppMessageFullHeaderLabel
  • ABKInAppMessageFullMessageLabel

The above properties are available as of iOS SDK v2.12.1.

Method Declarations

For additional information see the following header files:

Implementation Samples

See InAppMessageTestViewController.m file and CustomInAppMessageViewController.m in the Stopwatch sample app.

Troubleshooting

Troubleshooting Scenarios

Expected In-App Message Did Not Display

Most in-app message issues can be broken down into two main categories: delivery and display. To troubleshoot why an expected in-app message did not display on your device, you should first ensure that the in-app message was delivered to the device, then troubleshoot message display.

Impression or Click Analytics Aren’t Being Logged

If you have set an in-app message delegate to manually handle message display or click actions, you’ll need to manually log clicks and impressions on the in-app message.

Impressions Are Lower Than Expected

Triggers take time to sync to the device on session start, so there can be a race condition if users log an event or purchase right after they start a session. One potential workaround could be changing the campaign to trigger off of session start, then segmenting off of the intended event or purchase. Note that this would deliver the in-app message on the next session start after the event has occurred.

In-App Message Delivery

The SDK requests in-app messages from Appboy’s servers on session start. To check if in-app messages are being delivered to your device, you’ll need to ensure that in-app messages are being both requested by the SDK and returned by Appboy’s servers.

Check If Messages Are Requested and Returned

  1. Add yourself as a test user on the Dashboard.
  2. Set up an in-app message campaign targeted at your user.
  3. Ensure that a new session occurs in your application.
  4. Use the Event User Logs to check that your device is requesting in-app messages on session start. Find the SDK Request associated with your test user’s session start event.
    • If your app was meant to request triggered In-App Messages, you should see trigger in the Requested Responses field under Response Data.
    • If your app was meant to request Original In-App Messages, you should see in_app in the Requested Responses field under Response Data.
  5. Use the Event User Logs to check if the correct in-app messages are being returned in the Response Data.

In-App Message

Troubleshoot Messages Not Being Requested

If your in-app messages are not being requested, your app might not be tracking sessions correctly, as in-app messages are refreshed upon session start. Also be sure that your app is actually starting a session based on your app’s session timeout semantics:

Session Start

Troubleshoot Messages Not Being Returned

If your in-app messages are not being returned, you’re likely experiencing a campaign targeting issue:

  • Your segment does not contain your user.
    • Check your user’s Engagement tab to see if the correct segment appears under Segments.
  • Your user has previously received the in-app message and was not re-eligible to receive it again.
    • Check the campaign re-eligibility settings under the Delivery tab of the Campaign Composer and make sure the re-eligibility settings align with your testing setup.
  • Your user hit the frequency cap for the campaign.
  • If there was a control group on the campaign, your user may have fallen into the control group.
    • You can check if this has happened by creating a segment with a “Received Campaign Variant” filter, where the campaign variant is set to “Control”, and checking if your user fell into that segment.
    • When creating campaigns for integration testing purposes, make sure to opt-out of adding a control group.

In-App Message Display

If your app is successfully requesting and receiving in-app messages but they are not being shown, some device-side logic may be preventing display:

  • Triggered in-app messages are rate-limited based on the minimum time interval between triggers, which defaults to 30 seconds.
  • If you have set a delegate to customize in-app message handling, check your delegate to ensure it is not affecting in-app message display.
  • Failed image downloads will prevent in-app messages with images from displaying. Image downloads will always fail if the SDWebImage framework is not integrated properly. Check your device logs to ensure that image downloads are not failing.
  • If the device orientation did not match the orientation specified by the in-app message, the in-app message will not display. Make sure that your device is in the correct orientation.

News Feed

The News Feed is a fully customizable in-app content feed for your users. Our targeting and segmentation allows you to create a stream of content that is individually catered to the interests of each user. Depending on their position in the user life cycle and the nature of your app, this could be an on-boarding content server, an advertisement center, an achievement center, or a generic news center. The News Feed supports GIF display.

Example News Feed: Urban Outfitters

iOS ActivityFeed+FeedBack

News Feed Integration Overview

Integrating the ViewController ABKFeedViewController will display the Appboy News Feed.

You have a great deal of flexibility in how you choose to display the view controllers. There are several different versions of the view controllers to accommodate different navigation structures.

Time Estimate: 10 Minutes

Note: The News Feed that is called by the default behavior of a slideup in-app message on click will not respect any delegates that you set for the News Feed. If you want to respect that, you must set the delegate on ABKInAppMessageController and implement the ABKInAppMessageControllerDelegate delegate method onInAppMessageClicked:.

News Feed View Controller Integration Options

The news feed can be integrated with 4 view controller contexts, either in code or via a storyboard implementation.

Generic Context – ABKFeedViewControllerGenericContext

  • The most basic type of FeedViewController, which can be used in:
    • A tab view
    • A master-detail view
    • Any other context where dismissal or navigation buttons are not needed
ABKFeedViewControllerGenericContext *genericFeed =
[[ABKFeedViewControllerGenericContext alloc] init];
  • You can set the instance’s title and navigation items before pushing it into a navigation controller
ABKFeedViewControllerNavigationContext *navFeed =
[[ABKFeedViewControllerNavigationContext alloc] init];
  • Used to present the view controller in a modal view, with a navigation bar on top and a Done button on the right side of the bar
  • Set the modal’s title via the navigationBarTitle property
  • If a delegate is NOT set the Done button will dismiss the modal view
  • If a delegate is set the Done button will call the delegate, and the delegate itself will be responsible for dismissing the view
ABKFeedViewControllerModalContext *modalFeed = [[ABKFeedViewControllerModalContext alloc] init];
[self presentViewController:modalFeed animated:YES completion:nil];

Popover Context – ABKFeedViewControllerPopoverContext

  • Used when the view controller is presented in a popover, with a navigation bar on top and a “Done” button on the right side of the bar.
  • You must implement a delegate to allow the “Done” button to dismiss the popover. Users can also dismiss the popover by clicking outside of the view. You can change the title by setting the navigationBarTitle property.
// Present a feedViewController in a popover
ABKFeedViewControllerPopoverContext *popoverFeed = [[ABKFeedViewControllerPopoverContext alloc] init];
// We want to be notified when either "Cancel" or "Send" is tapped.
popoverFeed.delegate = self;
self.feedpopoverController =
        [[UIPopoverController alloc] initWithContentViewController:popoverFeed];

Note: There is a header file for ABKFeedViewController in the AppboyKit folder. This is not meant to be instantiated directly; rather, you should use one of the context-specific view controllers listed above. The Stopwatch sample project contains examples of the view controllers in both Storyboard and code. Refer to the SDK documentation for more information.

Storyboard Implementation

The Appboy view controllers can also be integrated using Storyboards. Check out the Stopwatch Sample App in the iOS SDK for an example. Note: These implementations must be viewed within Xcode.

News Feed View Controller Delegate (SDK v2.7+)

As of Appboy iOS SDK v2.7, there is a delegate method that allows you to customize the behavior after a card click action:

- (BOOL) onCardClicked:(ABKCard *)clickedCard feedViewController:(UIViewController *)newsFeed;

This is useful for opening URLs in your own UIWebView or mobile Safari. To implement this method, you must assign the delegate object to your ABKFeedViewController’s Appboy delegate:

@property (assign, nonatomic) id<ABKFeedViewControllerDelegate> appboyDelegate;

For more information see ABKFeedViewControllerDelegate.h. The Stopwatch sample application also has an example of setting the delegate for an instance of ABKFeedViewControllerModalContext in FeedAndFeedbackUIViewController.m.

Defining a News Feed Category

Instances of the Appboy News Feed can be configured to only receive cards from a certain “category”. This allows for effective integration of multiple news feed streams within a single application. For more information on this feature see Appboy Academy.

News Feed Categories can be defined by calling the following methods as you load the News Feed:

[newsFeed setCategories:ABKCardCategoryAll];
[newsFeed setCategories:ABKCardCategoryAnnouncements];
[newsFeed setCategories:ABKCardCategoryAdvertising];
[newsFeed setCategories:ABKCardCategorySocial];
[newsFeed setCategories:ABKCardCategoryNews];
[newsFeed setCategories:ABKCardCategoryNoCategory];

You can also populate a feed with a combination of categories as in the following example:

[newsFeed setCategories:ABKCardCategoryAnnouncements|ABKCardCategoryAdvertising];

Sample Code

Below are examples of how to integrate the Appboy view controllers into your app, including a one-button combined News Feed and Feedback integration example.

One Button Feed and Feedback Integration - Modal View

ABKFeedViewControllerNavigationContext *streamModal = [[ABKFeedViewControllerNavigationContext alloc] init];
self.modalNavigationController = [[UINavigationController alloc] initWithRootViewController:streamModal];
UIBarButtonItem *feedbackBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Feedback"
    style:UIBarButtonItemStyleBordered target:self action:@selector(openFeedbackFromModalFeed:)];
streamModal.navigationItem.leftBarButtonItem = feedbackBarButtonItem;
UIBarButtonItem *closeButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Close"
    style:UIBarButtonItemStyleBordered target:self action:@selector(closeModalNavigationView:)];
streamModal.navigationItem.rightBarButtonItem = closeButtonItem;
[self presentModalViewController:self.modalNavigationController animated:YES];

One Button Feed and Feedback Integration - Popover View

ABKFeedViewControllerNavigationContext *streamModal = [[ABKFeedViewControllerNavigationContext alloc] init];
self.popoverNavigationController = [[UINavigationController alloc] initWithRootViewController:streamModal];
self.popoverNavigationController.delegate = self;
UIBarButtonItem *feedbackBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Feedback"
    style:UIBarButtonItemStyleBordered target:self action:@selector(openFeedbackFromModalFeed:)];
streamModal.navigationItem.rightBarButtonItem = feedbackBarButtonItem;
self.streamPopover = [[UIPopoverController alloc]
  initWithContentViewController:self.popoverNavigationController];

Popover View Animations

To improve the feedback form animations in the popover view, we recommend overriding the navigation controller delegate method. For example:

#pragma mark
    #pragma navigation controller delegate method
    - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(
          UIViewController *)viewController animated:(BOOL)animated {
      if ([viewController isKindOfClass:[ABKFeedbackViewControllerNavigationContext class]]) {
        [UIView animateWithDuration:0.5 animations:^{
          viewController.contentSizeForViewInPopover = CGSizeMake(320, 300);
        }];
      }
    }

Implementation Examples

ABKFeedViewControllerNavigationContext and ABKFeedViewControllerModalContext are utilized within FeedAndFeedbackUIViewController.m in the Stopwatch sample application. For further details see the ABKFeedViewController header files.

Note: You should only implement the FeedViewController using the contexts as outlined above. Never directly.

Requesting Unread Card Count

News Feed Badge Example

Badges are a great way to call attention to new content awaiting your users in the News Feed. If you’d like to add a badge to your news feed, the Appboy SDK provides methods to query the following:

  • Unread News Feed Cards for the current user
  • Total Viewable News Feed Cards for the current user

The method declarations in ABKFeedController below describe this in detail:

/*!
 * This method returns the number of currently active cards which have not been viewed in the given categories.
 * A "view" happens when a card becomes visible in the feed view.  This differentiates
 * between cards which are off-screen in the scrolling view, and those which
 * are on-screen; when a card scrolls onto the screen, it's counted as viewed.
 *
 * Cards are counted as viewed only once -- if a card scrolls off the screen and
 * back on, it's not re-counted.
 *
 * Cards are counted only once even if they appear in multiple feed views or across multiple devices.
 */
- (NSInteger) unreadCardCountForCategories:(ABKCardCategory)categories;

/*!
 * This method returns the total number of currently active cards belongs to given categories. Cards are
 * counted only once even if they appear in multiple feed views.
 */
- (NSInteger) cardCountForCategories:(ABKCardCategory)categories;

Displaying Number of Unread News Feed Items on App Badge Count

Badge Example

In addition to serving as push notification reminders for an app, badges can also be utilized to denote unviewed items in the user’s news feed. Updating the badge count based off unread news feed updates can be a valuable tool in attracting users back to your app and increasing sessions.

Call this method which records the badge count once the app is closed and the user’s session ends.

(void)applicationDidEnterBackground:(UIApplication *)application

Within the above method, implement the following code which actively updates the badge count while the user views cards during a given session.

[UIApplication sharedApplication].applicationIconBadgeNumber = [[Appboy sharedInstance].feedController unreadCardCountForCategories:ABKCardCategoryAll];

For more information see the Appboy.h header file. The Stopwatch sample application also has a sample implementation of a badge in FeedAndFeedbackUIViewController.m, as well as code clearing the badge count in AppDelegate.m.

Refreshing the News Feed (SDK v2.7+)

As of SDK v2.7, you can manually request Appboy to refresh the user’s News Feed in Appboy.h using - (void) requestFeedRefresh;. For example:

[[Appboy sharedInstance] requestFeedRefresh];

For more information see the Appboy.h header file.

Theming the News Feed

Appboy’s iOS SDK supports powerful theming options based on the NUI customization framework. NUI allows you customize almost everything about the look and feel of an app using style sheets similar to CSS.

Appboy has its own specific set of UI elements which must be customized independently from the rest of your app – they do not inherit property settings from the standard NUI classes. This allows you to customize Appboy without affecting the rest of your app.

Getting Started - Theming & NUI

To theme your app, integrate NUI into your project by adding pod 'NUI' in your Podfile, or follow these instructions.

Add this line to - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions in your AppDelegate.m file:

[NUISettings initWithStylesheet:@"Your_Theme"];
[Appboy sharedInstance].useNUITheming = YES;

First, add #import #NUISettings.h" to your bridging header file.

Next, add this line to application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool in your AppDelegate.swift file:

NUISettings.initWithStylesheet("Your_Theme")
Appboy.sharedInstance().useNUITheming = true

NUI allows you to customize the behavior of many UI elements, such as labels, buttons, views, and text fields. For each component, there is a set of properties you can set via a style sheet – NUIStyle.nss – which you include in your project.

In navigation contexts, the navigation bar and its buttons cannot be themed using the Appboy NUI classes. This limitation occurs because the navigation context view controller does not own its navigation bar when it is the child of a navigation context. Instead, properties of the navigation bar are set at the parent level.

There are two ways to theme the navigation bar and buttons in a navigation context:

  • Programmatically set properties directly in the parent view controller
  • Use standard NUI classes to theme all navigation bars in your app

General Warnings

  • If you are using a navigation bar in a non-ARC app, ensure that your NSNotificationCenter removes it as an observer when it is released or deallocated. NUI adds all navigation bars as an observer for the custom method orientationDidChange, so in a non-ARC app you will need to manually remove the observer. Otherwise, you will receive a SIGSEGV error that the notification center tried to post a notification to a released object.
[[NSNotificationCenter defaultCenter] removeObserver:yourNavigationBar];
  • Due to a bug in how NUI parses the .nss file, setting text-shadow-offset to a single number, instead of two comma separated numbers, will cause a crash
  • In iOS 6.1, if you do no customize UIButton, buttons will show up as just the title text with no background; in other words, customizing UIButton is mandatory if you use NUI.
    • This issue is not related to Appboy; this is a general problem with NUI
    • Put an entry for the Button style class in your NUIStyle.nss style sheet

      Button {
      background-color: #FFFFFF;
      corner-radius: 8;
      }
      

Themable News Feed Elements

Subsequent sections have example UI screenshots of the feedback form and news feed. In the screenshots, all the customizable UI elements are pointed out with their custom classes. These class names are directly used in NUIStyle.nss. For the attributes that can be set for each class, please refer to the theming directory below.

iOS NUI Cards

Full List Customizable Elements & Associated Properties

ABKFeedCloseBarButton
background-color
background-color-top/background-color-bottom
background-color-highlighted
background-image
background-image-insets
background-tint-color
border-color
border-width
corner-radius
font-color
font-name
font-size
text-shadow-color
text-shadow-offset
ABKFeedBarButtonBack
font-name
font-color
font-size
background-image
background-image-insets
ABKFeedNavigationBar
bar-tint-color
font-color
font-name
font-size
text-shadow-color
text-shadow-offset
title-vertical-offset
background-tint-color
background-image
background-image-insets
ABKFeedBackgroundView
background-color
background-image
border-color
border-width
corner-radius
ABKFeedItemTabView
background-color
background-image
border-color
border-width
corner-radius
ABKFeedItemBackgroundView
background-color
background-image
shadow-color
shadow-opacity
border-color
border-width
corner-radius
width
ABKFeedItemTitleBackgroundView
background-color
background-image
border-color
border-width
corner-radius
width
ABKFeedItemTabLabel
background-color
border-color
border-width
corner-radius
font-color
font-name
font-size
text-align
text-alpha
text-shadow-color
text-shadow-offset
text-auto-fit
font-size
ABKFeedItemStaticLinkLabel
background-color
border-color
border-width
corner-radius
font-color
font-name
font-size
text-align
text-alpha
text-shadow-color
text-shadow-offset
text-auto-fit
font-size
ABKFeedItemBodyLabel
background-color
border-color
border-width
corner-radius
font-color
font-name
font-size
text-align
text-alpha
text-shadow-color
text-shadow-offset
text-auto-fit
font-size
ABKFeedItemTitleLabel
background-color
border-color
border-width
corner-radius
font-color
font-name
font-size
text-align
text-alpha
text-shadow-color
text-shadow-offset
text-auto-fit
font-size
ABKFeedItemSubtitleLabel
background-color
border-color
border-width
corner-radius
font-color
font-name
font-size
text-align
text-alpha
text-shadow-color
text-shadow-offset
text-auto-fit
font-size
ABKFeedItemPurchaseButton
background-color-top/background-color-bottom
background-image
background-image-insets
background-image-highlighted
background-image-highlighted-insets
border-color
border-width
corner-radius
font-name
font-color
text-align
text-alpha
text-shadow-color
text-shadow-offset

Style Value Types

  • Boolean - A boolean (true or false)
  • BorderStyle - A border style, as rendered by a UITextBorderStyle. Accepted values are none, line, bezel, and rounded
  • Box - A series of 1 to 4 integers that specify the widths of a box’s edges. Interpreted like CSS’s padding and margin properties (top, right, bottom, left). Examples:
    • 15 (a box with a width of 15 for each edge),
    • 10 15 (a box with a width of 10 for the top and bottom edges and 15 for the right and left edges)
  • Color - A hex color (e.g. #FF0000), rgba, hsl, or hsla expression (e.g. rgb(255,0,0) or hsla(0.5, 0, 1.0, 0.5)); or a color name that UIColor has a related method name for (e.g. red, yellow, clear). If [UIColor fooColor] is supported, then foo is supported.
  • FontName - A font name (see available values here)
  • Gradient - Two Colors that will create a vertical gradient. background-color-top and background-color-bottom need to be defined in separate .nss properties
  • Image - A name of an image, as used in [UIImage imageNamed:name] (e.g. MyImage.png)
  • Number - A number (e.g. -1, 4.5)
  • Offset - Two numbers comprising the horizontal and vertical values of an offset (e.g. -1,1)
  • TextAlign - A text alignment (e.g. left, right, center)
  • VerticalAlign - A vertical alignment (e.g. top, center, bottom, fill)

Implementation Examples

See the AppDelegate.m file and StopwatchNUIStyle.nss in the Stopwatch sample app.

Refer to the useNUITheming documentation for more information.

News Feed Data Model (SDK v2.7+)

The News Feed data model is available in the iOS SDK as of v2.7.

Getting the Data

To access the News Feed data model, subscribe to News Feed update events:

// Subscribe to feed updates
// Note: you should remove the observer where appropriate
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(feedUpdated:)
                                             name:ABKFeedUpdatedNotification
                                           object:nil];
// Called when the feed is refreshed (via `requestFeedRefresh`)
- (void)feedUpdated:(NSNotification *)notification {
  BOOL updateIsSuccessful = [notification.userInfo[ABKFeedUpdatedIsSuccessfulKey] boolValue];
  // check for success
  // get the cards using [[Appboy sharedInstance].feedController getCardsInCategories:ABKCardCategoryAll];
}
// Subscribe to feed updates
// Note: you should remove the observer where appropriate
NotificationCenter.default.addObserver(self, selector:
  #selector(feedUpdated),
  name:NSNotification.Name.ABKFeedUpdated, object: nil)
// Called when the feed is refreshed (via `requestFeedRefresh`)
private func feedUpdated(_ notification: Notification) {
  if let updateSuccessful = notification.userInfo?[ABKFeedUpdatedIsSuccessfulKey] as? Bool {
    // check for success
    // get the cards using Appboy.sharedInstance()?.feedController.getCardsInCategories(.all);      
  }
}

If you want to change the card data after it’s been sent by Appboy, we recommend storing (deep copy) the card data locally, updating the data and displaying yourself. The cards are accessible via ABKFeedController.

Base Card Model

Appboy has five unique card types that share a base model. Each type of card also has additional properties that are specific to each card which are listed below.

Base Card Model Properties

  • idString (read only) - The card’s ID set by Appboy
  • viewed - This property reflects if the card is read or unread by the user
  • created (read only) - The property is the unix timestamp of the card’s creation time from Appboy dashboard
  • updated (read only) - The property is the unix timestamp of the card’s latest update time from Appboy dashboard
  • categories - The list of categories assigned to the card, cards without a category will be assigned ABKCardCategoryNoCategory
  • extras - An optional NSDictionary of NSString values.

Categories (SDK v2.7+)

  • ABKCardCategoryNoCategory
  • ABKCardCategoryNews
  • ABKCardCategoryAdvertising
  • ABKCardCategoryAnnouncements
  • ABKCardCategorySocial
  • ABKCardCategoryAll

In addition to the base card properties:

  • image (required) - This property is the URL of the card’s image
  • url (optional) - The URL that will be opened after the card is clicked on. It can be a http(s) URL or a protocol URL
  • domain (optional) - The link text for the property url, like @”blog.appboy.com”. It can be displayed on the card’s UI to indicate the action/direction of clicking on the card, but is hidden in the default Appboy News Feed.

Captioned Image Properties

In addition to the base card properties:

  • image (required) - This property is the URL of the card’s image
  • title (required) - The title text for the card
  • description (required) - The body text for the card
  • url (optional) -The URL that will be opened after the card is clicked on. It can be a http(s) URL or a protocol URL
  • domain (optional) - The link text for the property url, like @”blog.appboy.com”. It can be displayed on the card’s UI to indicate the action/direction of clicking on the card.

Text Announcement (Captioned Image without Image) Properties

In addition to the base card properties:

  • title (required) - The title text for the card
  • description (required) - The body text for the card
  • url (optional) -The URL that will be opened after the card is clicked on. It can be a http(s) URL or a protocol URL
  • domain (optional) - The link text for the property url, like @”blog.appboy.com”. It can be displayed on the card’s UI to indicate the action/direction of clicking on the card.

Classic Card Properties

In addition to the base card properties:

  • image (required) - This property is the URL of the card’s image
  • title (optional) - The title text for the card
  • description (required) - The body text for the card
  • url (optional) -The URL that will be opened after the card is clicked on. It can be a http(s) URL or a protocol URL
  • domain (optional) - The link text for the property url, like @”blog.appboy.com”. It can be displayed on the card’s UI to indicate the action/direction of clicking on the card.

Cross Promotion (Small) Properties

In addition to the base card properties:

  • mediaType - The type of iTunes media
    • ItunesAlbum
    • ItunesAudiobook
    • ItunesCompilation
    • ItunesEbook
    • ItunesFeatureMovie
    • ItunesPodcast
    • ItunesSoftware
    • ItunesSong
    • ItunesTvEpisode
    • ItunesTvSeason
  • title - The title text for the card. This will be the promoted item’s name.
  • subtitle - The text of the category of the promoted item
  • image - This property is the URL of the card’s image.
  • iTunesId - The iTunes ID number of the promoted item
  • rating (required for mediaType ItunesSoftware, optional otherwise) - The rating of the promoted app. This property will be 0.0 unless the promoted item is an app, and the rating will be in the range of [0.0, 5.0];
  • price - The number of reviews of the promoted app. This property will be 0 unless the promoted item is an app.
  • reviews - This property is the text that will be displayed in the tag on the top of the small cross promotion card.
  • caption - The itunes url of the promoted item which leads to the item’s App Store page.
  • url - The itunes url of the promoted item which leads to the item’s App Store page.
  • universal (optional) - This property indicates if the promoted item is universal or not.

Card Methods:

  • logCardImpression - Manually log an impression to Appboy for a particular card.
  • logCardClicked - Manually log a click to Appboy for a particular card. The SDK will only log a card click when the card has the url property with a valid value. All sublcasses of ABKCard have the url property.

Specific Card Dimensions:

Card titles can be no longer than two lines long. Titles longer than two lines will be abbreviated with an ellipsis (…). Card widths and all text lengths (with the exceptions of titles and subtitles) are dynamic. Aspect ratios are defined as width/height.

Note: Fixed card image ratios are no longer required as of version 2.8.1 of the iOS SDK.

News

Image Aspect Ratio: 1 Image/Card Ratio (image’s width/card’s width): 0.177

Image Aspect Ratio: 5.988

Captioned Image

Image Aspect Ratio: 1.333

Cross Promotion Card

Tab Height (the “Recommended” tab): 22.0 Tab lines: 1 Image Aspect Ratio: 1 Image/Card Ratio: 0.177 Purchase Button Width: 60.0 Subtitle lines: 1

Log Feed Display

As of SDK v2.7, when displaying the news feed in your own user interface, you can manually record news feed impressions via - (void) logFeedDisplayed;. For example:

[[Appboy sharedInstance] logFeedDisplayed];

Analytics

Session Tracking

The Appboy SDK reports session data that is used by the Appboy dashboard to calculate user engagement and other analytics integral to understanding your users. Based on the below session semantics, our SDK generates “start session” and “close session” data points that account for session length and session counts viewable within the Appboy Dashboard.

Session Lifecycle

A session is started when you call [[Appboy sharedInstance] startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions], after which by default sessions start when the UIApplicationWillEnterForegroundNotification notification is fired (i.e. the app enters the foreground) and end when the app leaves the foreground (i.e. when the UIApplicationDidEnterBackgroundNotification notification is fired or when the app dies).

Note: If you need to force a new session, you can do so by changing users.

Customizing Session Timeout

To customize the session timeout, set the ABKSessionTimeoutKey so that the value is a number of seconds in the Appboy initialization method startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions.

// Sets the session timeout to 60 seconds
[Appboy startWithApiKey:@"YOUR-API_KEY"
          inApplication:application
      withLaunchOptions:options
      withAppboyOptions:@{ ABKSessionTimeoutKey : @(60) }];
// Sets the session timeout to 60 seconds
Appboy.startWithApiKey("YOUR-API-KEY",
                       inApplication:application,
                       withLaunchOptions:launchOptions,
                       withAppboyOptions:[ ABKSessionTimeoutKey : 60 ]])

If you have set a session timeout, then the above session semantics all extend to that customized timeout.

Testing Session Tracking

To detect sessions via your user, find your user on the dashboard and navigate to “App Usage” on the user profile. You can confirm that session tracking is working by checking that the “Sessions” metric increases when you would expect it to.

test_session

Setting User IDs

User IDs should be set for each of your users. These should be unchanging and accessible when a user opens the app. A database ID or a hashed email address/username is usually a good reference to use. We strongly recommend providing this identifier as it will allow you to:

  • Track your users across devices and platforms, improving the quality of your behaviorial and demographic data.
  • Import data about your users using our User Data API.
  • Target specific users with our Messaging API for both general and transactional messages.

Note: If such an identifier is not available, Appboy will assign a unique identifier to your users, but you will lack the capabilities above. You should avoid setting User IDs for users for whom you lack a unique identifier that is tied to them as an individual. Passing a device identifier offers no benefit versus the automatic anonymous user tracking Appboy offers by default.

Note: These User IDs should be private and not easily obtained (e.g. not a plain email address or username).

Assigning a User ID

Time Estimate: 3 Minutes

You should make the following call as soon as the user is identified (generally after logging in) in order to set the user id:

[[Appboy sharedInstance] changeUser:@"YOUR_USER_ID_STRING"];
Appboy.sharedInstance().changeUser("YOUR_USER_ID")

Note: Do not call changeUser() when a user logs out. changeUser() should only be called when the user logs into the application. Setting changeUser() to a static default value will associate ALL user activity with that default “user” until the user logs in again. Additionally, we recommend against changing the user ID when a user logs out, as it makes you unable to target the previously logged-in user with reengagement campaigns. If you anticipate multiple users on the same device, but only want to target one of them when your app is in a logged out state, we recommend separately keeping track of the user ID you want to target while logged out and switching back to that user ID as part of your app’s logout process.

Implementation Example

changeUser is utilized in UserAttributesViewController.m file of the Stopwatch sample app.

Automatic Preservation of Anonymous User History

Identification Context Preservation Behavior
User has not been previously identified Anonymous history is merged with user profile upon identification
User has been previously identified in-app or via API Anonymous history is not merged with user profile upon identification

Additional Notes and Best Practices

Please note the following:

  • If your app is used by multiple people, you can assign each user a unique identifier to track them.
  • Once a user ID has been set, you cannot revert that user to an anonymous profile
  • Do Not change the user ID upon a user “log out”.
    • Doing so separates the device from the user profile. You will be unable to target the previously logged out user with re-engagement messages. If you anticipate multiple users on the same device, but only want to target one of them when your app is in a logged out state, we recommend separately keeping track of the user ID you want to target while logged out and switching back to that user ID as part of your app’s logout process. By default, only the last user that was logged in will receive push notifications from your app.
  • Switching from one identified user to another is a relatively costly operation.
    • When you request the user switch, the current session for the previous user is automatically closed and a new session is started. Furthermore, Appboy will automatically make a data refresh request for the news feed, slideup and other Appboy resources for the new user.
  • Note: If you opt to use a hash of a unique identifier as your userID take care to ensure that you’re normalizing the input to your hashing function.
    • e.g. If you’re going to use a hash of an email address, ensure that you’re stripping leading and trailing whitespace from the input, and taking localization problems into account.

Aliasing Users

An alias serves as an alternative unique user identifier. Use aliases to identify users along different dimensions than your core user ID:

  • Set a consistent identifier for analytics that will follow a given user both before and after they have logged in to a mobile app or website.
  • Add the identifiers used by a third party vendor to your Appboy users in order to more easily reconcile your data externally.

Each alias consists of two parts: a name for the identifier itself, and a label indicating the type of alias. Users can have multiple aliases with different labels, but only one name per label.

 [[Appboy sharedInstance].user addAlias:ALIAS_NAME withLabel:ALIAS_LABEL];
Appboy.sharedInstance().user.addAlias(ALIAS_NAME, ALIAS_LABEL);

Tracking Custom Events

You can record custom events in Appboy to learn more about your app’s usage patterns and to segment your users by their actions on the dashboard.

Before implementation, be sure to review examples of the segmentation options afforded by Custom Events vs. Custom Attributes vs Purchase Events in our Best Practices section.

Adding A Custom Event

Time Estimate: 5 Minutes

[[Appboy sharedInstance] logCustomEvent:@"YOUR_EVENT_NAME"];
Appboy.sharedInstance().logCustomEvent("YOUR_EVENT_NAME");

Adding Properties

You can add metadata about custom events by passing an NSDictionary populated with NSNumber, NSString, or NSDate values.

[[Appboy sharedInstance] logCustomEvent:@"YOUR_EVENT_NAME" withProperties:@{@"key1":"value1"}];
Appboy.sharedInstance().logCustomEvent("YOUR_EVENT_NAME", withProperties:["key1":"value1"]);

See our class documentation for more information.

Reserved Keys

The following keys are RESERVED and CANNOT be used as Custom Event Properties:

  • time
  • product_id
  • quantity
  • event_name
  • price
  • currency

Implementation Example

logCustomEvent is utilized within EventsViewController.m file in the Stopwatch sample application.

Setting Custom Attributes

Appboy provides methods for assigning attributes to users. You’ll be able to filter and segment your users according to these attributes on the dashboard.

Before implementation, be sure to review examples of the segmentation options afforded by Custom Events vs. Custom Attributes vs Purchase Events in our Best Practices section.

Time Estimate: 5 Minutes

Assigning Standard User Attributes

To assign user attributes, you need to set the appropriate field on the shared ABKUser object. For example, to assign the current user’s first name to be “Jeff,” you would use the following line of code:

[Appboy sharedInstance].user.firstName = @"Jeff";
Appboy.sharedInstance().user.firstName = "Jeff"

The following attributes should be set on the ABKUser object:

  • firstName
  • lastName
  • email
  • dateOfBirth
  • country
  • homeCity
  • bio
  • phone
  • userID
  • avatarImageURL
  • twitterAccountIdentifier
  • gender

We strongly recommend collecting email addresses even if you’re not sending emails through Appboy. Email makes it easier to search for individual user profiles and troubleshoot issues as they arise.

Assigning Custom User Attributes

Beyond the attributes above, Appboy also allows you to define Custom Attributes using a number of different data types: For more information regarding the segmentation options each of these attributes will afford you see our “Best Practices” documentation within this section.

Custom Attribute with a Boolean Value

[[Appboy sharedInstance].user setCustomAttributeWithKey:@"your-attribute-string" andBOOLValue:yourBOOLValue];
Appboy.sharedInstance().user.setCustomAttributeWithKey("your-attribute-string", andBOOLValue: yourBoolValue)

Custom Attribute with an Integer Value

[[Appboy sharedInstance].user setCustomAttributeWithKey:@"your-attribute-string" andIntegerValue:yourIntegerValue];
Appboy.sharedInstance().user.setCustomAttributeWithKey("your-attribute-string", andIntegerValue: yourIntegerValue)

Custom Attribute with a Double Value

[[Appboy sharedInstance].user setCustomAttributeWithKey:@"your-attribute-string" andDoubleValue:yourDoubleValue];
Appboy.sharedInstance().user.setCustomAttributeWithKey("your-attribute-string", andDoubleValue: yourDoubleValue)

Note: Appboy treats FLOAT and DOUBLE values exactly the same within our database.

Custom Attribute with a String Value

[[Appboy sharedInstance].user setCustomAttributeWithKey:@"your-attribute-string" andStringValue:"Your String"];
Appboy.sharedInstance().user.setCustomAttributeWithKey("your-attribute-string", andStringValue: "Your String")

Custom Attribute with a Date Value

[[Appboy sharedInstance].user setCustomAttributeWithKey:@"your-attribute-string" andDateValue:yourDateValue];
Appboy.sharedInstance().user.setCustomAttributeWithKey("your-attribute-string", andDateValue:yourDateValue)

Note: Dates passed to Appboy with this method must either be in the ISO 8601 format, e.g 2013-07-16T19:20:30+01:00 or in the yyyy-MM-dd'T'HH:mm:ss.SSSZ format e.g 2016-12-14T13:32:31.601-0800

Custom Attribute with an Array Value

Note: The maximum number of elements in Custom Attribute Arrays defaults to 25. The maximum for individual arrays can be increased to up to 100 in the Appboy Dashboard, under “Manage App Group -> Custom Attributes”. Arrays exceeding the maximum number of elements will be truncated to contain the maximum number of elements. For more information on Custom Attribute Arrays and their behavior, see our Documentation on Arrays.

// Setting a custom attribute with an array value
[[Appboy sharedInstance].user setCustomAttributeArrayWithKey:@"array_name" array:@[@"value1",  @"value2"]];
// Adding to a custom attribute with an array value
[[Appboy sharedInstance].user addToCustomAttributeArrayWithKey:@"array_name" value:@"value3"];
// Removing a value from an array type custom attribute
[[Appboy sharedInstance].user removeFromCustomAttributeArrayWithKey:@"array_name" value:@"value2"];
// Removing an entire array and key
[[Appboy sharedInstance].user setCustomAttributeArrayWithKey:@"array_name" array:nil];
// Setting a custom attribute with an array value
Appboy.sharedInstance().user.setCustomAttributeArrayWithKey("array_name", array: ["value1",  "value2"])
// Adding to a custom attribute with an array value
Appboy.sharedInstance().user.addToCustomAttributeArrayWithKey("array_name", value: "value3")
// Removing a value from an array type custom attribute
Appboy.sharedInstance().user.removeFromCustomAttributeArrayWithKey("array_name", value: "value2")

Unsetting a Custom Attribute

Custom Attributes can also be unset using the following method:

[[Appboy sharedInstance].user unsetCustomAttributeWithKey:@"your-attribute-string"];
Appboy.sharedInstance().user.unsetCustomAttributeWithKey("your-attribute-string")

Incrementing/Decrementing Custom Attributes

This code is an example of an incrementing custom attribute. You may increment the value of a custom attribute by any positive or negative integer or long value.

[[Appboy sharedInstance].user incrementCustomUserAttribute:@"Attribute Key" by:incrementIntegerValue];
Appboy.sharedInstance().user.incrementCustomUserAttribute("Attribute Key", by: incrementIntegerValue)

Setting a Custom Attribute via the REST API

You can also use our REST API to set user attributes. To do so refer to the user API documentation.

Custom Attribute Value Limits

Custom attribute values have a maximum length of 255 characters; longer values will be truncated.

Implementation Example

User Attributes are set within the UserAttributesViewController.m file within the Stopwatch sample application.

Setting Up User Subscriptions

To set up a subscription for your users (either email or push), call the functions setEmailNotificationSubscriptionType or setPushNotificationSubscriptionType, respectively. Both of these functions take the enum type ABKNotificationSubscriptionType as arguments. This type has three different states:

Subscription Status Definition
ABKOptedin Subscribed, and explicitly opted in
ABKSubscribed Subscribed, but not explicitly opted in
ABKUnsubscribed Unsubscribed and/or explicitly opted out
  • Note: Users who grant permission for an app to send them push notifications are defaulted to the status of ABKOptedin as iOS requires an explicit optin.
  • Users will be set to ABKSubscribed automatically upon receipt of a valid email address, however we suggest that you establish an explicit opt-in process and set this value to OptedIn upon reciept of explicit consent from your user. See Appboy Academy for details.

Setting Email Subscriptions

[[Appboy sharedInstance].user setEmailNotificationSubscriptionType: ABKNotificationSubscriptionType]
Appboy.sharedInstance().user.setEmailNotificationSubscriptionType(ABKNotificationSubscriptionType)

Setting Push Notification Subscriptions

[[Appboy sharedInstance].user setPushNotificationSubscriptionType: ABKNotificationSubscriptionType]
Appboy.sharedInstance().user.setPushNotificationSubscriptionType(ABKNotificationSubscriptionType)

Note: Users who grant permission for an app to send them push notifications are defaulted to the status of ABKOptedin as iOS requires an explicit optin.

For more information on implementing subscriptions, visit the topic on Appboy Academy.

Logging Purchases

Record in-app purchases so that you can track your revenue over time and across revenue sources, as well as segment your users by their lifetime value.

Appboy supports purchases in multiple currencies. Purchases that you report in a currency other than USD will be shown in the dashboard in USD based on the exchange rate at the date they were reported.

Before implementation, be sure to review examples of the segmentation options afforded by Custom Events vs. Custom Attributes vs Purchase Events in our Best Practices section.

Tracking Purchases & Revenue

Time Estimate: 3-5 Minutes

To use this feature, add this method call after a successful purchase in your app:

[[Appboy sharedInstance] logPurchase:@"your product ID"
inCurrency:@"USD"
atPrice:[[[NSDecimalNumber alloc] initWithString:@"0.99"] autorelease]];
Appboy.sharedInstance().logPurchase("your product ID", inCurrency: "USD", atPrice: NSDecimalNumber(string: "0.99"))
  • Supported currency symbols include: USD, CAD, EUR, GBP, JPY, AUD, CHF, NOK, MXN, NZD, CNY, RUB, TRY, INR, IDR, ILS, SAR, ZAR, AED, SEK, HKD, SPD, DKK, and TWD.
    • Any other provided currency symbol will result in a logged warning and no other action taken by the SDK.
  • The product ID can have a maximum of 255 characters

Adding Properties

You can add metadata about purchases by passing an NSDictionary populated with NSNumber, NSString, or NSDate values.

Please see the iOS Class Documentation for additional details.

Adding Quantity

You can add a quantity to your purchases if customers make the same purchase multiple times in a single checkout. You can accomplish this by passing in a NSUInteger for the quantity. Please see the iOS Class Documentation for additional details.

Note: If you pass in a value of 10 USD, and a quantity of 3 then that will log to the user’s profile as 3 purchases of 10 dollars for a total of 30 dollars. Quantity must be less than or equal to 100.

[[Appboy sharedInstance] logPurchase:@"your product ID"
inCurrency:@"USD"
atPrice:[[[NSDecimalNumber alloc] initWithString:@"0.99"] autorelease]
withProperties:@{@"key1":"value1"}];
Appboy.sharedInstance().logPurchase("your product ID", inCurrency: "USD", atPrice: NSDecimalNumber(string: "0.99"), withProperties: ["key1":"value1"])

See the Technical Documentation for more information.

Reserved Keys

The following keys are RESERVED and CANNOT be used as Purchase Properties:

  • time
  • product_id
  • quantity
  • event_name
  • price
  • currency

Implementation Example

logPurchase is utilized within the EventsViewController.m file in the Stopwatch sample application.

REST API

You can also use our REST API to record purchases. Refer to the user API documentation for details.

Social Data Tracking

Collecting Social Account Data

The Appboy iOS SDK no longer automatically collects Facebook user data starting with version 2.10, and does not collect Twitter user data automatically with version 2.13. If you want to integrate Facebook user data in Appboy user profiles, you need to fetch the user’s data and pass it to Appboy.

You can get a user’s Facebook and Twitter data from the iOS system. You can also refer to the sample code for accessing Facebook accounts in class FacebookViewController, and Twitter account in class TwitterViewController in our Stopwatch sample application. If you were previously relying on the deprecated promptUserForAccessToSocialNetwork: method, see promptUserToConnectFacebookAccountOnDeviceAndFetchAccountData and promptUserToConnectTwitterAccountOnDeviceAndFetchAccountData for sample code on manually prompting your users for access to their social account data.

Another way to get a user’s Facebook data is from Facebook’s iOS SDK. For more information about integrating the Facebook SDK, follow the steps in Facebook SDK documentation.

Passing Facebook Data To Appboy

Initialize ABKFacebookUser objects with the Facebook data you have collected and pass it to Appboy:

ABKFacebookUser *facebookUser = [[ABKFacebookUser alloc] initWithFacebookUserDictionary:self.facebookUserProfile numberOfFriends:self.numberOfFacebookFriends likes:self.facebookLikes];
  [Appboy sharedInstance].user.facebookUser = facebookUser;
var facebookUser : ABKFacebookUser = ABKFacebookUser(facebookUserDictionary: facebookUserDictionary, numberOfFriends: numberOfFriends, likes: likes);
Appboy.sharedInstance().user.facebookUser = facebookUser;

Note: In ABKFacebookUser’s init method initWithFacebookUserDictionary:numberOfFriends:likes:, all the parameters should be dictionaries and arrays returned directly from Facebook:

Parameter Definition
facebookUserProfile The dictionary returned from the endpoint “/me”.
numberOfFriends The length of the friends array returned from the endpoint “/me/friends”.
likes The array of user’s Facebook likes from the endpoint “/me/likes”.

Note: For additional information regarding the Facebook Graph API, please refer to the Facebook Graph API Developer Documentation.

Additionally, you can tailor what Facebook data you’re sending to Appboy, in case you don’t want to include the entire basic profile. For example:

ABKFacebookUser *facebookUser = [[ABKFacebookUser alloc] initWithFacebookUserDictionary:facebookUserPublicProfile numberOfFriends:-1 likes:nil];  

Passing Twitter Data To Appboy

Initialize ABKTwitterUser objects, set up the Twitter data you have collected and pass it to Appboy:

ABKTwitterUser *twitterUser = [[ABKTwitterUser alloc] init];
twitterUser.userDescription = self.userDescription;
twitterUser.twitterID = self.twitterID;
[Appboy sharedInstance].user.twitterUser = twitterUser;
var twitterUser : ABKTwitterUser = ABKTwitterUser();
twitterUser.userDescription = twitterDserDescription;
twitterUser.twitterID = twitterID;
Appboy.sharedInstance().user.twitterUser = twitterUser;

Recording Social Network Shares

As of SDK v.2.16, logSocialShare: has been deprecated. If you were relying on this method to log social shares, you can use logCustomEvent: instead.

Location Tracking

By default, Appboy enables location tracking after the host application has gained permission from the user. Provided that users have opted into location tracking, Appboy will log a single location for each user on session start.

Requesting Location Tracking Permissions

The allowRequestWhenInUseLocationPermission method gives Appboy permission to request WhenInUse authorization on your behalf the next time the application attempts to collect location in the foreground:

[[Appboy sharedInstance].locationManager allowRequestWhenInUseLocationPermission];
Appboy.sharedInstance().locationManager.allowRequestWhenInUseLocationPermission()

Logging A Single Location

To log a single location using Appboy’s location manager, use the following method:

[[Appboy sharedInstance].locationManager logSingleLocation];
Appboy.sharedInstance().locationManager.logSingleLocation()

Disabling Automatic Location Tracking

You can disable automatic location tracking at app startup time via the startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions method. In the appboyOptions dictionary, set ABKDisableAutomaticLocationCollectionKey to YES. For example:

[Appboy startWithApiKey:@"YOUR-API_KEY"
          inApplication:application
      withLaunchOptions:options
      withAppboyOptions:@{ ABKDisableAutomaticLocationCollectionKey : @(YES) }];
Appboy.startWithApiKey("YOUR-API-KEY",
inApplication:application,
withLaunchOptions:launchOptions,
withAppboyOptions:[ ABKDisableAutomaticLocationCollectionKey : true ]])

Manually Enabling iOS Location Targeting

If you wish to use your own CLLocationManager instead of Appboy’s provided location manager, you can follow the steps below to manually enable location targeting in your iOS application. Once location tracking is enabled, you can use Appboy’s methods to manually pass location tracking information along to Appboy.

Setting Up Location Tracking

  1. Click on the target for your project (using the left-side navigation), and select the “Build Phases” tab.
  2. Click the button under “Link Binary With Libraries”
  3. In the menu, select CoreLocation.framework
  4. Mark this library as required using the pull-down menu next to CoreLocation.framework
  5. Add NSLocationWhenInUserUsageDescription and/or NSLocationAlwaysUsageDescription as keys to your plist.
    • The value should be a string, which will be displayed when the system asking permission from your users.
  6. See the following sample code to authorize CLLocationManager so Appboy can collect location from your app:
@property (retain, nonatomic) CLLocationManager *locationManager;

- (void)startLocationUpdates {
  // Create the location manager if this object does not
  // already have one.
  if (self.locationManager == nil) {
    CLLocationManager \*locationManager = [[CLLocationManager alloc] init];
    self.locationManager = locationManager;
    [locationManager release];
  }

  self.locationManager.delegate = self;
  self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;

  // Set a movement threshold for new events.
  self.locationManager.distanceFilter = 500; // meters

  if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
    [self.locationManager requestWhenInUseAuthorization];
  }
  /* When you want to request authorization even when the app is in the background, use requestAlwaysAuthorization.
   if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
    [self.locationManager requestAlwaysAuthorization];
  } \*/
  [self.locationManager startUpdatingLocation];
}

#pragma location manager delegate method
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
  // test that the horizontal accuracy does not indicate an invalid measurement
  if (newLocation.horizontalAccuracy < 0) return;
  // test the age of the location measurement to determine if the measurement is cached
  // in most cases you will not want to rely on cached measurements
  NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
  if (locationAge > 5.0) return;
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
  // The location "unknown" error simply means the manager is currently unable to get the location.
  if ([error code] != kCLErrorLocationUnknown) {
    [self stopUpdatingLocation];
  }
}

- (void)stopUpdatingLocation {
  [self.locationManager stopUpdatingLocation];
  self.locationManager.delegate = nil;
}

For additional details please see this helpful blog post.

Passing Location Data to Appboy

The following two methods can be used to set the last known location for the user. Keep in mind that these methods are intended for use only where Appboy’s automatic location tracking has been disabled (i.e., ABKDisableAutomaticLocationCollectionKey has been set to YES).

[[Appboy sharedInstance].user setLastKnownLocationWithLatitude:latitude
                                                     longitude:longitude
                                            horizontalAccuracy:horizontalAccuracy];

[[Appboy sharedInstance].user setLastKnownLocationWithLatitude:latitude
                                                     longitude:longitude
                                            horizontalAccuracy:horizontalAccuracy
                                                      altitude:altitude
                                              verticalAccuracy:verticalAccuracy];

For more information, see ABKUser.h.

Implementation Examples

AppDelegate.m in the Stopwatch sample application shows how to authorize Appboy to request location authorization on your behalf, and MiscViewController.m gives an example of logging location data.

Uninstall Tracking

Uninstall Tracking utilizes background push notifications with an Appboy flag in the payload. For more information, see our Uninstall Tracking page on Appboy Academy.

Implementing Uninstall Tracking

Step 1: Enabling background push

Make sure that you have enabled the “Remote notifications” option from the “Background Modes” section of your Xcode project’s Capabilities tab. For additional details, refer to our documentation on Silent Push Notifications.

Step 2: Checking for Appboy background push

Appboy uses background push notifications to collect uninstall tracking analytics. Follow the instructions here to ensure that your application does not take any unwanted actions upon receiving Appboy’s uninstall tracking notifications.

Step 3: Test from the Dashboard

To ensure that your app does not take any unwanted automatic actions upon receiving an Appboy uninstall tracking push, send yourself a test push from the Dashboard.

  1. On the Campaigns page, create a Push Notification campaign.

    Push Notification

    Select iOS Push as your platform.

  2. On the Additional Message Settings page,
    • Add the key appboy_uninstall_tracking with corresponding value true
    • Check “Add Content-Available Flag”

    Key/Value Pair

  3. Use the Preview Message page to send yourself a test uninstall tracking push.

    Test User

  4. Check that your app does not take any unwanted automatic actions upon receiving the push.

Note: The above steps are a proxy for sending an uninstall tracking push from the Dasboard. If you have badge counts enabled, a badge number will be sent along with the test push, but Appboy’s uninstall tracking pushes will not set a badge number on your application.

Step 4: Enable Uninstall Tracking

Follow the instructions for enabling Uninstall Tracking on Appboy Academy.

Customer Feedback

The Appboy feedback form allows users to submit feedback about your app that is immediately sent to your company’s dashboard.

Time Estimate: 10 Minutes

Feedback Integration Overview

iOS ActivityFeed+FeedBack

Integrating the ViewController FeedbackViewController will show a feedback form and allow users to post Feedback to Appboy.

If your app has a Facebook ID, you can put it in your app’s plist under the key FacebookAppID. The Feedback page will automatically display the Facebook button when where is a Facebook account connected to the iOS device.

You have a great deal of flexibility in how you choose to display the view controllers. There are several different versions of the view controllers to accommodate different navigation structures.

Feedback Display Options

The feedback form can be integrated with 3 view controller contexts:

ABKFeedbackControllerModalContext presents the view controller in a modal with a navigation bar, “Cancel,” and “Send” buttons.

  • When a delegate is not set:
    • Both buttons will dismiss the modal view
  • When a delegate is set:
    • The “Cancel” button will call the delegate when it is tapped
    • The “Send” button will call the delegate when feedback is successfully sent
    • The delegate must dismiss the view itself
ABKFeedbackViewControllerModalContext *modalFeedback =
[[[ABKFeedbackViewControllerModalContext alloc] init] autorelease];
[self presentViewController:modalFeedback animated:YES completion:nil];
presentViewController(ABKFeedbackViewControllerModalContext.init(), animated:true, completion: nil)

ABKFeedbackControllerNavigationContext

  • A Feedback ViewController class for use in a navigation stack as a child of a UINavigationController. Includes a “Send” button.
  • When a delegate is set, the “Send” button will call it when feedback is successfully sent.
ABKFeedbackViewControllerNavigationContext *navFeedback =
[[[ABKFeedbackViewControllerNavigationContext alloc] init] autorelease];
[navigationController pushViewController:navFeedback animated:YES];

Popover Context

ABKFeedbackControllerPopoverContext Used when the view controller is presented in a popover and includes a navigation bar, “Cancel”, and “Send” button

  • You must set a delegate:
    • The “Cancel” button will call the delegate when it is tapped
    • The “Send” button will call the delegate when feedback is successfully sent
    • The delegate must dismiss the view itself
ABKFeedbackViewControllerPopoverContext *popoverFeedback = [[[ABKFeedbackViewControllerModalContext alloc] init] autorelease];
  // We want to be notified when either "Cancel" or "Send" is tapped.
popoverFeedback.delegate = self;
self.feedbackPopoverController = [[[UIPopoverController alloc] initWithContentViewController:popoverFeedback] autorelease];

The following two methods are required – they are how we know when to close the popover. Tapping outside the feedback popover will NOT close it. This is by designm we don’t want the user to accidentally tap outside the popover and close it if there’s text in the feedback form.

(void) feedbackViewControllerPopoverContextFeedbackSent:(ABKFeedbackViewControllerPopoverContext *)sender {
  [self.contactUsPopoverController dismissPopoverAnimated:YES];
}

Improving Animations in Popover View

To improve the feedback form animations in the popover view, we recommend overriding the navigation controller delegate method. For example:

#pragma mark
    #pragma navigation controller delegate method
    - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(
          UIViewController *)viewController animated:(BOOL)animated {
      if ([viewController isKindOfClass:[ABKFeedbackViewControllerNavigationContext class]]) {
        [UIView animateWithDuration:0.5 animations:^{
          viewController.contentSizeForViewInPopover = CGSizeMake(320, 300);
        }];
      }
    }

Configuring User Feedback Response Dialog

We recommend that you use the FeedbackViewController delegate to display a confirmation in response to user actions. For example, to display an alert telling the user the feedback has been successfully sent, your delegate callback method might look like this:

- (void) feedbackViewControllerModalContextFeedbackSent
                 (ABKFeedbackViewControllerModalContext \*)sender {

  [self dismissModalViewControllerAnimated:YES];

  UIAlertView \*feedbackAlert =
     [[UIAlertView alloc] initWithTitle:@"Thanks for your feedback."
                                message:nil
                               delegate:nil
                      cancelButtonTitle:@"OK"
                      otherButtonTitles:nil];

  [feedbackAlert show];

  [feedbackAlert release];

}

Storyboard Implementation

The Appboy view controllers can also be integrated using Storyboards. Check out the Stopwatch Sample App in the iOS SDK for an example.

Sample Code

Below are examples of how to integrate the Appboy view controllers into your app. We’ve included a one-button integration example so you can have one call to open the feedback view controller from within the stream view. This works well for apps migrating from V1 who would like to maintain their current interactions.

One Button Integration - Modal View

ABKFeedViewControllerNavigationContext *streamModal = [[[ABKFeedViewControllerNavigationContext alloc] init]
    autorelease];
self.modalNavigationController = [[[UINavigationController alloc] initWithRootViewController:streamModal]
    autorelease];
UIBarButtonItem *feeddbackBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Feedback"
    style:UIBarButtonItemStyleBordered target:self action:@selector(openFeedbackFromModalFeed:)] autorelease];
streamModal.navigationItem.leftBarButtonItem = feeddbackBarButtonItem;
UIBarButtonItem *closeButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Close"
    style:UIBarButtonItemStyleBordered target:self action:@selector(closeModalNaviagationView:)] autorelease];
streamModal.navigationItem.rightBarButtonItem = closeButtonItem;
[self presentModalViewController:self.modalNavigationController animated:YES];

One Button Integration - Popover View

ABKFeedViewControllerNavigationContext *streamModal = [[[ABKFeedViewControllerNavigationContext alloc] init]
      autorelease];
self.popoverNavigationController = [[[UINavigationController alloc] initWithRootViewController:streamModal]
    autorelease];
self.popoverNavigationController.delegate = self;
UIBarButtonItem *feeddbackBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Feedback"
    style:UIBarButtonItemStyleBordered target:self action:@selector(openFeedbackFromModalFeed:)] autorelease];
streamModal.navigationItem.rightBarButtonItem = feeddbackBarButtonItem;
self.streamPopover = [[[UIPopoverController alloc]
  initWithContentViewController:self.popoverNavigationController] autorelease];

Popover View Animations

To improve the feedback form animations in the popover view, we recommend overriding the navigation controller delegate method. For example:

#pragma mark
    #pragma navigation controller delegate method
    - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(
          UIViewController *)viewController animated:(BOOL)animated {
      if ([viewController isKindOfClass:[ABKFeedbackViewControllerNavigationContext class]]) {
        [UIView animateWithDuration:0.5 animations:^{
          viewController.contentSizeForViewInPopover = CGSizeMake(320, 300);
        }];
      }
    }

Manual Feedback Collection

The following method will allow you to pass Feedback to Appboy from a form or field within your app. This is perfect for passing feedback from an existing UI element to Appboy. The method will return a boolean which indicates whether or not the feedback was queued for delivery.

  • replyToEmail: The user email address feedback replies will be directed towards.
  • message: The message input by the user. Must be non-null and non-empty.
  • isReportingABug: Flag which indicates whether or not the feedback describes a bug, or is merely a suggestion / question.
- (BOOL) submitFeedback:(NSString *)replyToEmail message:(NSString *)message isReportingABug:(BOOL)isReportingABug;
Appboy.sharedInstance().submitFeedback("USERS_EMAIL_ADDRESS", message: "USERS_FEEDBACK_STRING", isReportingABug: Bool)

When manually collecting feedback, you can also log when the form is displayed using - (void) logFeedbackDisplayed; in Appboy.h. For example:

[[Appboy sharedInstance] logFeedbackDisplayed];
Appboy.sharedInstance().logFeedbackDisplayed()

Third Party Provider Integrations

Appboy has easy integrations with both Desk.com and Zendesk. So long as you are collecting feedback through our ready-made UI or manually using the submitFeedback method, you can pass that feedback through to either third party provider. This will afford you the benefit of having the entire user profile card available to the CSR handling the case, and allow you to segment based upon the number of feedback requests a user has submitted.

To take advantage of these integrations, please visit the “feedback” section within the “app settings” page.

Implementation Examples

ABKFeedbackViewControllerNavigationContext and ABKFeedbackViewControllerModalContext are utilized within the FeedAndFeedbackUIViewController.m file in the Stopwatch Sample App.

Theming

Appboy’s built in feedback UI is themable via NUI. See the following section for details on how you can customize this UI to suit the look and feel of your app.

Using NUI

Appboy’s iOS SDK supports powerful theming options based on the NUI customization framework. NUI allows you customize almost everything about the look and feel of an app using style sheets similar to CSS.

Appboy has its own specific set of UI elements which must be customized independently from the rest of your app – they do not inherit property settings from the standard NUI classes. This allows you to customize Appboy without affecting the rest of your app.

To theme your app, integrate NUI into your project by adding pod 'NUI' in your Podfile, or follow these instructions.

Add this line to - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions in your AppDelegate.m file:

[NUISettings initWithStylesheet:@"Your_Theme"];
[Appboy sharedInstance].useNUITheming = YES;

Be sure to first add #import "NUISettings.h" to your bridging header file.

Add this line to application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool in your AppDelegate.swift file:

NUISettings.initWithStylesheet("Your_Theme")
Appboy.sharedInstance().useNUITheming = true

NUI allows you to customize the behavior of many UI elements, such as labels, buttons, views, and text fields. For each component, there is a set of properties you can set via a style sheet – NUIStyle.nss – which you include in your project.

Implementation Example

See the AppDelegate.m file and StopwatchNUIStyle.nss in the Stopwatch sample app.

Refer to the useNUITheming documentation for more information.

In navigation contexts, the navigation bar and its buttons cannot be themed using the Appboy NUI classes. This limitation occurs because the navigation context view controller does not own its navigation bar when it is the child of a navigation context. Instead, properties of the navigation bar are set at the parent level.

There are two ways to theme the navigation bar and buttons in a navigation context:

  • Programmatically set properties directly in the parent view controller
  • Use standard NUI classes to theme all navigation bars in your app

Warnings:

  • If you are using a navigation bar in a non-ARC app, ensure that your NSNotificationCenter removes it as an observer when it is released or deallocated. NUI adds all navigation bars as an observer for the custom method orientationDidChange, so in a non-ARC app you will need to manually remove the observer. Otherwise, you will receive a SIGSEGV error that the notification center tried to post a notification to a released object.
[[NSNotificationCenter defaultCenter] removeObserver:yourNavigationBar];
  • Due to a bug in how NUI parses the .nss file, setting text-shadow-offset to a single number, instead of two comma separated numbers, will cause a crash
  • In iOS 6.1, if you do no customize UIButton, buttons will show up as just the title text with no background; in other words, customizing UIButton is mandatory if you use NUI.
    • This issue is not related to Appboy; this is a general problem with NUI
    • Put an entry for the Button style class in your NUIStyle.nss style sheet

      Button {
       background-color: #FFFFFF;
       corner-radius: 8;
      }
      

Themable Feedback Elements & Associated Properties

Subsequent sections have example UI screenshots of the feedback form and news feed. In the screenshots, all the customizable UI elements are pointed out with their custom classes. These class names are directly used in NUIStyle.nss. For the attributes that can be set for each class, please refer to the theming directory below.

Feedback Page

Customizable elements and properties:

ABKFeedbackCancelBarButtonItem
background-color
background-color-top/background-color-bottom
background-color-highlighted
background-image
background-image-insets
background-tint-color
border-color
border-width
corner-radiu
font-color
font-name
font-size
text-shadow-color
text-shadow-offset
ABKFeedbackSendBarButtonItem
background-color
background-color-top/background-color-bottom
background-color-highlighted
background-image
background-image-insets
background-tint-color
border-color
border-width
corner-radiu
font-color
font-name
font-size
text-shadow-color
text-shadow-offset
ABKFeedbackNavigationBar
bar-tint-color
font-color
font-name
font-size
text-shadow-color
text-shadow-offset
title-vertical-offset
background-tint-color
background-image
background-image-insets
ABKFeedbackBackgroundView
background-color
background-image
border-color
border-width
corner-radius
ABKFeedbackEmailAddressBarView
background-color
background-image
border-color
border-width
corner-radius
ABKFeedbackMessageLabel
background-color
border-color
border-width
corner-radius
font-color
font-name
font-size
text-align
text-alpha
text-shadow-color
text-shadow-offset
text-auto-fit
font-size
ABKFeedbackIssueButton
background-color
background-color-top/background-color-bottom
background-color-highlighted
background-color-selected
background-color-selected-highlighted
background-image
background-image-insets
background-image-highlighted
background-image-highlighted-insets
background-image-selected
background-image-selected-insets
border-color
border-width
corner-radius
ABKFeedbackTextField
background-color
border-color
border-width
border-style
corner-radius
font-color
font-name
font-size
vertical-align
ABKFeedbackIssueLabel
background-color
border-color
border-width
corner-radius
font-color
font-name
font-size
text-align
text-alpha
text-shadow-color
text-shadow-offset
text-auto-fit
font-size
ABKFeedbackTextView
background-color
background-image
border-color
border-width
corner-radius
font-color
font-name
font-size
padding

Additionally, you can customize the checkmark graphic by adding your own checkmark.png to AppboyKit Resources.

Style Value Types

  • Boolean - A boolean (true or false)
  • BorderStyle - A border style, as rendered by a UITextBorderStyle. Accepted values are none, line, bezel, and rounded
  • Box - A series of 1 to 4 integers that specify the widths of a box’s edges. Interpreted like CSS’s padding and margin properties (top, right, bottom, left). Examples:
    • 15 (a box with a width of 15 for each edge),
    • 10 15 (a box with a width of 10 for the top and bottom edges and 15 for the right and left edges)
  • Color - A hex color (e.g. #FF0000), rgba, hsl, or hsla expression (e.g. rgb(255,0,0) or hsla(0.5, 0, 1.0, 0.5)); or a color name that UIColor has a related method name for (e.g. red, yellow, clear). If [UIColor fooColor] is supported, then foo is supported.
  • FontName - A font name (see available values here)
  • Gradient - Two Colors that will create a vertical gradient. background-color-top and background-color-bottom need to be defined in separate .nss properties
  • Image - A name of an image, as used in [UIImage imageNamed:name] (e.g. MyImage.png)
  • Number - A number (e.g. -1, 4.5)
  • Offset - Two numbers comprising the horizontal and vertical values of an offset (e.g. -1,1)
  • TextAlign - A text alignment (e.g. left, right, center)
  • VerticalAlign - A vertical alignment (e.g. top, center, bottom, fill)

Advanced Use Cases

Linking

Implementation

For information regarding what a deep link is, please see our FAQ Section. If you’re looking to implement deep links for the first time please see the documentation below.

Step 1: Registering A Scheme

The custom scheme must be stated in the Info.plist file. The navigation structure is defined by an array of dictionaries. Each of those dictionaries contain an array of strings.

Using Xcode edit your Info.plist file:

  1. Add a new key URL types. Xcode will automatically make this an array containing a dictionary called Item 0.
  2. Within Item 0, add a key URL identifier. Set the value to your custom scheme.
  3. Within Item 0, add a key URL Schemes. This will automatically be an array containing a string called Item 0.
  4. Set URL Schemes » Item 0 to your custom scheme.

Alternatively, if you wish to edit your info.plist file directly, you can follow the spec below:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>YOUR.SCHEME</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>YOUR.SCHEME</string>
        </array>
    </dict>
</array>
Step 2: Adding a Scheme Whitelist (iOS 9+)

Starting with iOS 9, apps are required to have a whitelist of custom schemes that the app is allowed to open. Attempting to call schemes outside of this list will cause the system to record an error in the device’s logs and the deep link will not open. An example of this error will look like:

<Warning>: -canOpenURL: failed for URL: “yourapp://deeplink” – error: “This app is not allowed to query for scheme yourapp”

For example, if an in-app message should open the Facebook app when it is tapped, the app has to have the Facebook custom scheme (fb) in the whitelist. Otherwise, the system will reject the deep link. Deep links that direct to a page or view inside your own app still require that your app’s custom scheme be listed in your app’s Info.plist.

You should add all the schemes that the app needs to deep link to in a whitelist in your app’s Info.plist with the key LSApplicationQueriesSchemes. For example:

<key>LSApplicationQueriesSchemes</key>
<array>
    <string>stopwatch</string>
    <string>facebook</string>
    <string>twitter</string>
</array>

For more information, refer to Apple’s documentation on the LSApplicationQueriesSchemes key.

Step 3: Implement a Handler

After activating your app, iOS will call the method application:handleOpenURL: (iOS 2.0-9.0) or application:openURL:options: (iOS 9.0+). The important argument is the NSURL object.

- (BOOL) application:(UIApplication *)application handleOpenURL:(NSURL *)url {
  NSString *path  = [url path];
  NSString *query = [url query];
  // Here you should insert code to take some action based upon the path and query.
  return YES;
}

Open News Feed

In order to use Universal Links, make sure you have added a registered domain to your app’s capabilities and have uploaded an apple-app-site-association file. Then implement the method application:continueUserActivity:restorationHandler: in your AppDelegate. For example:

- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
  restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
  if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
    NSURL *url = userActivity.webpageURL;
    // Handle url
  }
}

For more information, refer to Apple’s Universal Links documentation.

Note: The default Universal Link integration is not compatible with Appboy’s push notifications, in-app messages, or the News Feed. See our Linking Customization documentation to handle Universal Links within your application. Alternatively, we recommend using scheme-based deep links with push notifications, in-app messages and the News Feed.

App Transport Security (ATS)

iOS 9 introduced a breaking change affecting web URLs embedded in in-app messages, News Feed cards and push notifications.

ATS Requirements

From Apple’s documentation: “App Transport Security is a feature that improves the security of connections between an app and web services. The feature consists of default connection requirements that conform to best practices for secure connections. Apps can override this default behavior and turn off transport security.”

ATS is applied by default on iOS 9+. It requires that all connections use HTTPS and are encrypted using TLS 1.2 with forward secrecy. For Apple’s specifications, refer to “Requirements for Connecting Using ATS.” All images served by Appboy to end devices are handled by a content delivery network (“CDN”) that supports TLS 1.2 and is compatible with ATS.

Unless they are specified as exceptions in your application’s Info.plist, connections that do not follow these requirements will fail with errors that look something like this:

CFNetwork SSLHandshake failed (-9801)
Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made."
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

Note: ATS compliance is enforced for links opened within the mobile app (Appboy’s default handling of clicked links), and does not apply to sites opened externally via a web browser.

Handling ATS Requirements

You can handle ATS in one of the following three ways:

Your Appboy integration can satisfy ATS requirements most simply by ensuring that any existing links you drive users to (through in-app message/push campaigns or news feed cards) satisfy ATS requirements. While there are ways to bypass ATS restrictions, Appboy’s recommended best practices are to ensure that all linked URLs are ATS-compliant. Given Apple’s increasing emphasis on application security, the below-listed approaches to allowing ATS exceptions are not guaranteed to be supported by Apple moving forwards.

An SSL tool can help you pinpoint web server security issues. This SSL Server Test from Qualys, Inc. provides a line item specifically for Apple ATS 9 / iOS 9 compliance.

Partially Disable ATS

You can allow a subset of links with certain domains or schemes to be treated as exceptions to the ATS rules. Your Appboy integration will satisfy ATS requirements if every link you use in an Appboy messaging channel is either ATS compliant or handled by an exception.

To add a domain as an exception of the ATS, add following to your app’s Info.plist file:

<key>NSAppTransportSecurity</key>
<dict>
	<key>NSAllowsArbitraryLoads</key>
	<true/>
	<key>NSExceptionDomains</key>
	<dict>
		<key>example.com</key>
		<dict>
			<key>NSExceptionAllowsInsecureHTTPLoads</key>
			<false/>
			<key>NSIncludesSubdomains</key>
			<true/>
		</dict>
	</dict>
</dict>

For more information, please refer to Apple’s documentation on App Transport Security keys.

Disable ATS Entirely

You can turn off ATS entirely. Please note that this is not recommended practice, due to both lost security protections and future iOS compatibility. To disable ATS, insert the following in your app’s Info.plist file:

<key>NSAppTransportSecurity</key>
<dict>
	<key>NSAllowsArbitraryLoads</key>
	<true/>
</dict>

For more information about how to debug ATS failures, please refer to Tim Ekl’s blog “Shipping an App With App Transport Security.”

URL Encoding

As of Appboy iOS SDK v2.21.0, the SDK percent-encodes links to create valid NSURLs. All link characters that are not allowed in a properly formed URL, such as Unicode characters, will be percent escaped.

To decode an encoded link, use the NSString method stringByRemovingPercentEncoding. For example:

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options {
  NSString *urlString = url.absoluteString.stringByRemovingPercentEncoding;
  // Handle urlString
}

For an implementation example, take a look at application:openURL:sourceApplication:annotation: method in the AppDelegate.m file of our Stopwatch sample application.

Customization

Introduced in SDK v.2.29.0, the ABKURLDelegate protocol can be used to customize handling of URIs such as deep links, web URLs and Universal Links. To set the delegate during Appboy initialization, pass a delegate object to the ABKURLDelegateKey in the appboyOptions of startWithApiKey:inApplication:withAppboyOptions:. Appboy will then call your delegate’s implementation of handleAppboyURL:fromChannel:withExtras: before handling any URIs.

For more information, see ABKURLDelegate.h.

You can see an example implementation of handleAppboyURL:fromChannel:withExtras: in the AppDelegate.m of our Stopwatch sample application.

@interface AppDelegate : UIResponder<UIApplicationDelegate, ABKURLDelegate>
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [Appboy startWithApiKey:@"YOUR-API-KEY"
          inApplication:application
      withLaunchOptions:launchOptions
        withAppboyOptions:@{ABKURLDelegateKey : self}];  
}

- (void)handleUniversalLink:(NSURL *)url {
  NSString *urlString = [[userActivity.webpageURL absoluteString] stringByRemovingPercentEncoding];
  // Route users within your app based on the urlString
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
  if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
    [self handleUniversalLink:url];
  }
}

- (BOOL)handleAppboyURL:(NSURL *)url fromChannel:(ABKChannel)channel withExtras:(NSDictionary *)extras {
  if ([[url.host lowercaseString] isEqualToString:@"MY-WEB-DOMAIN.com"]) {
    // Handle Universal Links sent by the Appboy iOS SDK
    [self handleUniversalLink:url];
    return YES;
  }
  // Let Appboy handle links otherwise
  return NO;
}

Frequent Use Cases

Deep Linking to App Settings

iOS 8 introduced the ability to take users from your app into its page in the iOS Settings application. You can take advantage of UIApplicationOpenSettingsURLString to deep link users to Settings from Appboy’s push notifications, in-app messages and the News Feed.

  1. First, make sure your application is set up for either scheme-based deep links or Universal Links.
  2. Decide on a URI for deep linking to the Settings page (e.g., stopwatch://settings or https://www.appboy.com/settings).
  3. If you are using custom scheme-based deep links, add the following code to your application:openURL:options: method:
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  NSString *path  = [url path];
  if ([path isEqualToString:@"settings"]) {
    NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
    [[UIApplication sharedApplication] openURL:settingsURL];
  }
  return YES;
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
  let path = url.path
  if (path == "settings") {
    UIApplication.shared.openURL(URL(string:UIApplicationOpenSettingsURLString)!)
  }
  return true
}

Fine Network Traffic Control

Request Processing Policies

Appboy allows the user the option to finely control network traffic using the following protocols:

Automatic Request Processing

ABKRequestProcessingPolicy enum value: ABKAutomaticRequestProcessing

  • This is the default request policy value.
  • The Appboy SDK will automatically handle all server communication, including:
    • Flushing custom events and attributes data to Appboy’s servers
    • Updating the News Feed
    • Requesting new in-app messages
    • Posting feedback
  • Immediate server requests are performed when user-facing data is required for any of Appboy’s features, such as in-app messages.
  • To minimize server load, Appboy performs periodic flushes of new user data every few seconds.
  • Data can be manually flushed to Appboy’s servers at any time using the following method:

    [[Appboy sharedInstance] flushDataAndProcessRequestQueue];

Automatic Request Processing Except For Custom Event/Attribute Data Flushing

ABKRequestProcessingPolicy enum value: ABKAutomaticRequestProcessingExceptForDataFlush

  • This protocol is the same as Automatic Request Processing EXCEPT:
    • Custom attributes and custom event data is not automatically flushed to the server
  • Data can be manually flushed to Appboy’s servers at any time using the following method:

    [[Appboy sharedInstance] flushDataAndProcessRequestQueue];

Manual Request Processing

ABKRequestProcessingPolicy enum value: ABKManualRequestProcessing

Note: This mode is only recommended for advanced use cases. If you’re merely trying to control background flush behavior, consider using ABKAutomaticRequestProcessingExceptForDataFlush.

  • With the exception of network requests required for internal features, all network traffic is manually controlled. No other communication between the Appboy servers and the app will happen unless prompted.
  • Standard network requests (e.g., updating the News Feed, flushing custom events and attributes, etc.) are created and added to the network queue. However, server communication will not happen until the following method is called:

    [[Appboy sharedInstance] flushDataAndProcessRequestQueue];

    Upon calling the above method, queued network requests will be performed and user data flushed immediately.

  • While in Manual Request Processing mode, flushDataAndProcessRequestQueue must be called in order to flush all network-related activity in your app. For example:
    • Setting custom events and attributes
    • Data automatically collected by Appboy (e.g., push token, device data)
    • Analytics events such as starting and ending sessions, in-app message impressions, etc.
  • If the queue already contains a flush request for the current user, the new request will be merged into the pre-existing request such that only one request will be executed. This is done to minimize server load without impacting expected SDK behavior.
  • Appboy will still perform automatic network requests for internal features, such as Feedback, Liquid Templating in In-App Messages, Geofences, and Location Tracking. For more details, see the ABKRequestProcessingPolicy declaration in Appboy.h.

Setting the Request Processing Policy

Set Request Policy On Startup

These policies can be set at app startup time from the startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions method. In the appboyOptions dictionary, set the ABKRequestProcessingPolicyOptionKey to any of the following three ABKRequestProcessingPolicy enum values defined below:

typedef NS_ENUM(NSInteger, ABKRequestProcessingPolicy) {
  ABKAutomaticRequestProcessing,
  ABKAutomaticRequestProcessingExceptForDataFlush,
  ABKManualRequestProcessing
};
public enum ABKRequestProcessingPolicy : Int {
    case automaticRequestProcessing
    case automaticRequestProcessingExceptForDataFlush
    case manualRequestProcessing
}

Set Request Policy At Runtime

The request processing policy can also be set during runtime via the requestProcessingPolicy property on Appboy. For example:

// Sets the request processing policy to automatic (the default value)
[Appboy sharedInstance].requestProcessingPolicy = ABKAutomaticRequestProcessing;
// Sets the request processing policy to automatic (the default value)
Appboy.sharedInstance()!.requestProcessingPolicy = ABKRequestProcessingPolicy.automaticRequestProcessing

Manual Shutdown of In-Flight Server Communication

If at any time an “in-flight” server communication needs to be halted, you must call the following method:

[[Appboy sharedInstance] shutdownServerCommunication];

Note: After calling this method, you must reset the request processing mode back to Automatic. For this reason, we only recommend calling this if the OS if forcing you to stop background tasks or something similar.

Policy Regarding Network Requests by the SDK

Note: See the aforementioned enumeration values for more information on possible options. This value can be set at start-up as described above or at runtime.

@property (nonatomic, assign) ABKRequestProcessingPolicy requestProcessingPolicy;
Implementation Examples

MiscViewController.m in the Stopwatch sample application provides examples of changing the data request processing policy, as well as manually flushing data to Appboy.

Localization

Localization is supported in version 2.5+ of the Appboy iOS SDK. In addition to English, Appboy supports 29 languages in our built-in SDK messages. These pertain to the default messages displayed in applications integrated with Appboy, such as places in the app that request feedback (“Please enter a feedback message”) or when there are connectivity issues (“Cannot establish network connection. Please try again later.”) See below for a full list of messages (strings). If the phone language is set to one of the supported languages, any of the Appboy default strings triggered within an integrated application will automatically appear in that language.

Languages Supported

  1. Arabic
  2. Burmese
  3. Chinese - Simplified
  4. Chinese - Traditional
  5. Danish
  6. Dutch
  7. Estonian
  8. Finnish
  9. French
  10. German
  11. Hindi
  12. Indonesian
  13. Italian
  14. Japanese
  15. Khmer
  16. Korean
  17. Lao
  18. Malay
  19. Norwegian
  20. Polish
  21. Portuguese - Brazil
  22. Portuguese - Portugal
  23. Russian
  24. Spanish - Latin America
  25. Spanish - Spain
  26. Swedish
  27. Tagalog
  28. Thai
  29. Vietnamese

List of Localized Strings

  • Free
  • Reporting an Issue?
  • Message
  • Email
  • An email address is required.
  • Cancel
  • Please enter a feedback message.
  • Empty Feedback Message
  • Feedback
  • Send
  • Invalid Email Address
  • Please enter a valid email address and try again.
  • We have no updates. Please check again later.
  • Connection Error
  • Cannot establish network connection. Please try again later.
  • Cannot establish network connection.
  • Please try again later.
  • Provide contact info from:
  • or
  • Done
  • Enable Facebook Connection
  • To re-enable Facebook, go to Settings > Facebook.
  • Enable Twitter Connection
  • To re-enable Twitter, go to Settings > Twitter.
  • OK
  • Hh: mm (hour:minute format)
  • $%.2f (price format)

Technical Details

For your convenience our CocoaPod integrates the LocalizedAppboyUIString.strings files for the aforementioned languages. If you do not wish to use one or more of these languages, you can feel free to delete these files from your project.

Optionally, you can also override any of the following Key / String pairs within your app’s Localizable.strings file much like a CSS override.

Localization String File Example

/*General Appboy Alarm Messages*/
"Appboy.alert.connect-facebook.title" = "Enable Facebook Connection";
"Appboy.alert.connect-facebook.message" = "To re-enable Facebook, go to Settings -> Privacy -> Facebook.";
"Appboy.alert.connect-twitter.title" = "Enable Twitter Connection";
"Appboy.alert.connect-twitter.message" = "To re-enable Twitter, go to Settings -> Privacy -> Twitter.";
"Appboy.alert.cancel-button.title" = "OK";
/*Feedback No Connection Messages*/
"Appboy.feedback.no-connection.title" = "Unable to Establish\n Network Connection";
"Appboy.feedback.no-connection.message" = "Please try again later.";
/* Feedback Alert Invalid Email Messages */
"Appboy.feedback.alert.invalid-email.title" = "Invalid Email Address";
"Appboy.feedback.alert.invalid-email.message" = "Please enter a valid email address and try again.";
/*Feedback Alert Empty Feedback Labels*/
"Appboy.feedback.alert.empty-feedback.title" = "Empty Feedback Message";
"Appboy.feedback.alert.empty-feedback.message" = "Please enter a feedback message.";
/*Feedback Modal Context Labels*/
"Appboy.feedback.modal-context.title" = "Feedback";
"Appboy.feedback.cancel-button.title" = "Cancel";
"Appboy.feedback.send-button.title" = "Send";
"Appboy.feedback.label.message" = "Message";
"Appboy.feedback.label.required" = "Required";
"Appboy.feedback.label.report-issue" = "Reporting an Issue?";
"Appboy.feedback.label.provide-contact-info" = "Provide contact info from:";
"Appboy.feedback.label.contact-info-required" = "Required";
"Appboy.feedback.label.contack-info-or" = "or";
"Appboy.feedback.email-text-field-place-hold" = "Email (Required)";
/*News Feed Default Labels*/
"Appboy.feed.card.cross-promotion.price.free" = "FREE";
"Appboy.feed.card.cross-promotion.price.format" = "$%.2f";
"Appboy.feed.done-button.title" = "Done";
"Appboy.feed.no-card.text" = "We have no updates.\nPlease check again later.";
"Appboy.feed.no-connection.title" = "Connection Error";
"Appboy.feed.no-connection.message" = "Cannot establish network connection.\nPlease try again later.";
/*Web View Default Button Labels*/
"Appboy.slideup.webview.done-button.title" = "Done";

For more information see the Apple Localization Developer Docs as well as the LOC standard language list.

Manual SDK Integration

Time Estimate: 20-30 Minutes

Step 1: Cloning the Appboy SDK

  1. Clone the Appboy iOS SDK Github project:
# This command will clone both versions of the Appboy SDK
$ git clone git@github.com:Appboy/appboy-ios-sdk.git
  1. In Xcode, from the project navigator, select the destination project or group for Appboy
  2. Navigate to File > Add Files to “Project_Name”
  3. Add the AppboyKit folder to your project as a group.
    • Make sure that the “Copy items into destination group’s folder” option is checked if you are integrating for the first time. In Xcode 7+, expand “Options” in the file picker to select “Copy items if needed” and “Create groups.”
  4. (Optional) If you are one of the following:
    • You only want the core analytics features of the SDK and do not use any UI features (e.g, In-App Messages, News Feed, or Feedback)
    • You have custom UI for Appboy’s UI features and handle the image downloading yourself

    You can use the core version of the SDK by removing the file ABKSDWebImageProxy.m and Appboy.bundle. This will remove the SDWebImage framework dependency and all the UI related resources (e.g. Nib files, images, localization files) from the SDK.

    Note: If you try to use the core version of the SDK without Appboy’s UI features, in-app messages will not display. Trying to display Appboy’s News Feed and Feedback UI with core version will lead to unpredictable behavior.

Step 2: Adding Required iOS Libraries

  1. Click on the target for your project (using the left-side navigation), and select the “Build Phases” tab
  2. Click the button under “Link Binary With Libraries”
  3. In the menu, select SystemConfiguration.framework
  4. Mark this library as required using the pull-down menu next to SystemConfiguration.framework
  5. Repeat to add each of the following required frameworks to your project, marking each as “required”
    • QuartzCore.framework
    • libz.dylib, or libz.tbd in Xcode 7+
    • CoreImage.framework
    • CoreText.framework
    • SystemConfiguration.framework
    • WebKit.framework
  6. Add the following frameworks and mark them as optional:
    • CoreTelephony.framework
    • Social.framework
    • Accounts.framework
    • AdSupport.framework
    • StoreKit.framework
  7. The SDWebImage framework is required for the Appboy News Feed and In-App Messaging to function properly. SDWebImage is used for image downloading and displaying, including GIFs. If you intend to use the News Feed or In-App Messages, please follow the integration instructions found on the SDWebImage Github Page.

    NOTE: From version 2.26.0, Appboy iOS SDK only supports 4.x version of SDWebImage. If you have to use SDWebImage version 3.x, please use Appboy SDK version 2.25.0 or below.

  8. The FLAnimatedImage framework is required to display GIF images in Appboy News Feed and In-App Messages. Please follow the integration instructions found on the FLAnimatedImage Github Page.

Optional Location Tracking

  1. Add the CoreLocation.framework to enable location tracking
  2. You must authorize location for your users using CLLocationManager in your app

Step 3: Updating your App Delegate

Add the following line of code to your AppDelegate.m file:

#import "AppboyKit.h"

Within your AppDelegate.m file, add the following snippet within your application:didFinishLaunchingWithOptions method:

Note: Be sure to update YOUR-API-KEY with the correct value from your App Settings page.

[Appboy startWithApiKey:@"YOUR-API-KEY"
         inApplication:application
     withLaunchOptions:launchOptions];

Note: Be sure to initialize Appboy in your application’s main thread.

See the AppDelegate.m file in the Stopwatch sample app.

If you do not have a bridging header file, create one and name it your-product-module-name-Bridging-Header.h by choosing File > New > File > (iOS or OS X) > Source > Header File. Then add the following line of code to the top of your bridging header file: #import "AppboyKit.h"

In your project’s Build Settings, add the relative path of your header file to the Objective-C Bridging Header build setting under Swift Compiler - Code Generation.

For more information about using Objective-C code in Swift projects, please refer to the Apple Developer Docs.

In AppDelegate.swift, add following snippet within function application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool:

Appboy.startWithApiKey("YOUR-API-KEY", inApplication:application, withLaunchOptions:launchOptions)

SDK Integration Complete

Appboy should now be collecting data from your application and your basic integration should be complete. Please see the following sections in order to enable custom event tracking, push messaging, the news-feed and the complete suite of Appboy features.

Full iOS class documentation is available to provide additional guidance on any of the aforementioned methods.

Beacon Integration

Here we will walk through how to integrate specific kinds of beacons with Appboy to allow for segmentation and messaging.

Gimbal Beacons

Once you have your Gimbal Beacons set up and integrated into your app, you can log Custom Events for things like a visit starting or ending, or a beacon being sighted. You can also log properties for these events, like the Place name or the Dwell time.

In order to log a Custom Event when a user enters a place, input this code into the didBeginVisit method:

[[Appboy sharedInstance] logCustomEvent:@"Entered %@", visit.place.name];
[[Appboy sharedInstance] flushDataAndProcessRequestQueue];

The flushDataAndProcessRequestQueue ensures that your event will log even if the app is in the background, and the same process can be implemented for leaving a location. Note that the above will create and increment a unique custom event for each new place that the user enters. As such, if you anticipate creating more than 50 places we recommend you create one generic “Place Entered” custom event and include the place name as an event property.

Locations & Geofences

Geofences are only available in select Appboy packages. For access please create a support ticket or speak with your Appboy Customer Success Manager. Learn more in the Appboy Academy.

To support geofences for iOS:

  1. Your integration must support background push notifications.

  2. Appboy location collection must not be disabled.

Note: On iOS, we are not strictly enforcing the Appboy request processing policy for geofences. When geofences are enabled, the requests will automatically be sent up even if the processing policy is manual processing.

Time Estimate: 15 Minutes

Step 1: Enable Background Push

To fully utilize our geofence syncing strategy you must have Background Push enabled in addition to completing the standard push integration.

Step 2: Check for Appboy Background Push

Appboy syncs geofences to devices using background push notifications. Follow the instructions here to ensure that your application does not take any unwanted actions upon receiving Appboy’s geofence sync notifications.

Step 3: Add NSLocationAlwaysUsageDescription to your Info.plist

Add the key NSLocationAlwaysUsageDescription to your info.plist with a String value that has a description of why your application needs to track location. This description will be shown when the system location prompt requests authorization and should clearly explain the benefits of location tracking to your users.

Step 4: Request authorization from the user

The Appboy iOS SDK can automatically request authorization from the user at app start if configured in our dashboard.

Otherwise, you can request authorization yourself and our SDK will wait until it has permission to start registering geofences.

Step 5: Enable Geofences on the Dashboard

iOS only allows up to 20 geofences to be stored for a given app. Appboy’s Locations product will use up some of these 20 available geofence slots. To prevent accidental or unwanted disruption to other geofence-related functionality in your app, location geofences must be enabled for individual Apps on the Dashboard.

For Appboy’s Locations product to work correctly, you should also ensure that your App is not using all available geofence spots.

Enable geofences from the Locations page:

Appboy Developer Console

Enable geofences from the App Settings page:

Appboy Developer Console

Sample Apps

Appboy’s SDKs each come with a sample application within the repository for your convenience. Each of these apps is fully buildable so you can test Appboy features alongside implementing them within your own applications. Testing behavior within your own application versus expected behavior and codepaths within the sample applications is an excellent way to debug any problems you may run into.

Building the Stopwatch Test Application

Appboy’s test application within the iOS SDK Github repository is called Stopwatch. Follow the instructions below to build a fully functional copy of it alongside your project.

  1. Create a new “App Group” and note the production API key.
  2. Place your production API key within the appropriate field in the AppDelegate.m file.

Note: Push notifications for the iOS test application requires additional configuration. See the iOS Push Documentation for details.