Initial SDK Setup

Installing the Braze 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 Braze 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.

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 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 9+, add the following line to your Podfile:

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

If you are using Xcode 8.3.3 or earlier, please use SDK version 3.0.2.

Note: We suggest you version Braze 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 Braze SDK automatically even with major changes, you can use pod 'Appboy-iOS-SDK' in your Podfile.

Note: If you do not use any Braze default UI and don’t want to introduce the SDWebImage dependency, please point your Braze 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 use_frameworks! in your Podfile, please see the Podfile within our HelloSwift Sample Application.

Step 3: Installing the Braze SDK

To install the Braze 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"

If you are integrating the Braze 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.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions)

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

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

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

Step 5: Specify Your Custom Endpoint or Data Cluster

If you are an EU-based client, have a custom endpoint or are on a specific data cluster, you will need to direct the SDK to use that endpoint for your integration to work correctly. EU clients should refer to this documentation to implement their iOS integration.

Starting with Braze iOS SDK v3.0.2, you can set a custom endpoint using the Info.plist file. Add the Appboy dictionary to your Info.plist file. Inside the Appboy dictionary, add the Endpoint string subentry and set the value to your custom endpoint url’s authority (e.g., not

In versions prior to 3.0.2, add the following class to your application, and then instantiate it and pass it in the appboyOptions dictionary you pass to startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions with the key: ABKAppboyEndpointDelegateKey

#import "Foundation/Foundation.h"
#import "ABKAppboyEndpointDelegate.h"

@interface AppboyEndpointDelegate : NSObject <ABKAppboyEndpointDelegate>

@implementation AppboyEndpointDelegate
- (NSString *) getApiEndpoint:(NSString *)appboyApiEndpoint {
    return [appboyApiEndpoint stringByReplacingOccurrencesOfString:@"" withString:@"YOUR_CUSTOM_ENDPOINT_OR_DATA_CLUSTER"];


Note: To find out your specific cluster or custom endpoint, please ask your Customer Success Manager or reach out to our support team.

Implementation Example

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

SDK Integration Complete

Braze 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 Braze features.

Updating the Braze SDK via CocoaPods

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

pod update

Customizing Braze On Startup

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

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

[Appboy startWithApiKey:@"YOUR-API-KEY"

In AppDelegate.swift, within your application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool method, add the following Braze 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 Braze Dashboard
  • application – The current app
  • launchOptions – The options NSDictionary that you get from application:didFinishLaunchingWithOptions:
  • appboyOptions – An optional NSDictionary with startup configuration values for Braze

See Appboy.h for a list of Braze 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" "3.4.0"

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

#import <Appboy_iOS_SDK/AppboyKit.h>


// In `application:didFinishLaunchingWithOptions:`
[Appboy startWithApiKey:@"YOUR-API-KEY"

If you want to use SDWebImage and/or FLAnimatedImage in your project along with the Braze SDK, you can install a thin version of the Braze Carthage framework. To do so, include the following lines in your Cartfile:

binary ""
github "rs/SDWebImage"
github "Flipboard/FLAnimatedImage"

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 Braze 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

Braze 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 Braze 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.

Verbose Logging

To enable verbose logging for debugging, add a dictionary named Appboy to your Info.plist file. Inside the Appboy Dictionary, add the LogLevel String subentry and set the value to “0”.

This feature is only intended to be used in development environments and should not be set in a released application.

Example Info.plist contents:


Push Notifications


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 Braze Academy page.

Basic Push Integration

Step 1: Configure Push Notifications

As described on this page,

  1. In your developer account, go to Certificates, Identifiers & Profiles.
  2. Under Keys, select All and click the Add button (+) in the upper-right corner.
  3. Under Key Description, enter a unique name for the signing key.
  4. Under Key Services, select the APNs checkbox, then click Continue. Click Confirm.
  5. Note the Key ID. Click Download to generate and download the key now.

Note: When you download the key, it is saved as a text file with a .p8 file extension. Save the file in a secure place because the key is not saved in your developer account and you won’t be able to download it again.

  1. Navigate to the app settings page in the dashboard and upload the .p8 file.
  2. When prompted, also enter your app’s Bundle Id, the Key Id, and your Team Id. Click Save.

Alternate Option: Using a .p12 Certificate (Legacy)

Alternately, you may utilize Apple’s older authentication scheme (.p12 SSL certificates). Unlike the .p8 solution described above, these certificates automatically expire every year and will require you to regenerate and re-upload them. Braze will send you email reminders as the certificate approaches expiration to help your notifications continue uninterrupted, but because this is a manual process we recommend utilizing the above-described .p8 authentication scheme instead. However, if you still wish to, you may configure and upload .p12 certificates as described here:

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


  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.
Export 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 Braze)
  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 2: Enable Push Capabilities

In your project settings, ensure that under the Capabilities tab your Push Notifications capability is toggled on, as described on this page. 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 3: 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.

Braze 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.")
} else {
  let types : UIUserNotificationType = [.alert, .badge, .sound]
  var setting : UIUserNotificationSettings = UIUserNotificationSettings(types:types, categories:nil)
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)

Step 4: Register Push Tokens With Braze

Once APNs registration is complete, the following method must be altered to pass the resulting deviceToken to Braze 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)

Note: The application:didRegisterForRemoteNotificationsWithDeviceToken: delegate method is called every time after [[UIApplication sharedApplication] registerForRemoteNotifications] is called. If you are migrating to Braze 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 5: Enable Push Handling

The following code passes received push notifications along to Braze 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

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

[[Appboy sharedInstance] userNotificationCenter:center

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 Braze will log a click for that push.

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

                                            didReceiveRemoteNotification: userInfo,
                                            fetchCompletionHandler: completionHandler)

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

                                               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 Braze 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 Braze 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

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

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

  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:

  didReceiveRemoteNotification: userInfo)

Step 6: 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 7: 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 Braze

Braze 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" : "",
       "type" : "jpg"
  "aps" :

The relevant payload values are:

// The Braze 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 a Braze 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 Braze 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.

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 Braze 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 Braze’s default push categories, follow the integration instructions below:

Step 1: Adding Braze Default Push Categories

Use the following code to register for Braze’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
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
// For UserNotification.framework
let appboyCategories = ABKPushUtils.getAppboyUNNotificationCategorySet()

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

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 Braze’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)


Badge Counts

You can specify the desired badge count when you compose a push notification through Braze’s dashboard. You may also update your badge count manually through your application’s applicationIconBadgeNumber property or through the remote notification payload. Braze will also clear the badge count when a Braze notification is received while the app is foregrounded. If you do not have a plan for clearing badges as part of normal app operation or by sending pushes which clear the badge, you should clear the badge when the app becomes active by adding 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, Braze supports custom notification categories and actions. Once you register categories in your application, you can use the Braze 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 that leverages the “LIKE_CATEGORY” displayed on the device!

Push Example with Buttons

Sample Braze Apple Push Payload

When Braze 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: {
    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, Braze 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 Braze’s Internal Push Notifications

Braze 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 a Braze feature that relies on internal push notifications (i.e., uninstall tracking or geofences), you may want to update your app to ignore Braze’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 Braze’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 Braze’s internal pushes triggering that because you would incur unnecessary network traffic. Furthermore, because Braze 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 Braze’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 Braze’s Internal Push Utility Methods

You can use the utility methods in ABKPushUtils to check if your app has received or was launched by a Braze internal push. isAppboyInternalRemoteNotification: will return YES on all Braze 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

Braze allows you to send additional key-value pairs along with a push notification 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.


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 "{\"api_key\":\"YOUR_API_KEY\",\"external_user_ids\":[\"YOUR_EXTERNAL_USER_ID\"],\"messages\":{\"apple_push\":{\"alert\":\"Test push\",\"extra\":{\"YOUR_KEY1\":\"YOUR_VALUE1\"}}}}"

Note: The above is an example for customers on the US-01 instance. If you are not on this instance please refer to our API documentation to see which endpoint to make requests to.


Understanding the Braze/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 Braze 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 Braze dashboard. The certificate allows Braze 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 Braze 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. As of SDK version 2.21.0, the iOS SDK will immediately and asynchronously send up the push token for apps using the default Automatic Flush Policy. 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 Braze 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 a Braze push campaign

When a push campaign is launched, Braze will make requests to APNs to deliver your message. Braze 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. Braze 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

Braze 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, Braze 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. Universal certificates may be configured in the Braze dashboard to send to either the development or production APNs environment. Using a development certificate for a production app or a production certificate for a development app 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.


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 Braze 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 Braze 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 Braze the push tokens.
  • Ensure that your push provisioning profile matches the environment you’re testing in. Universal certificates may be configured in the Braze dashboard to send to either the development or production APNs environment. Using a development certificate for a production app or a production certificate for a development app will not work.
  • Check that the push token you have uploaded to Braze matches the provisioning profile you used to build the app you sent the push token from.

Device Token Not For Topic

This error indicates that the push certificate and bundle ID for your app are mismatched. Check that the push certificate you have uploaded to Braze matches the provisioning profile used to build the app that the push token was sent 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.
  • Braze 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, Braze will not handle push notifications when the application state is UIApplicationStateActive. You should ensure that your app is not delaying calls to Braze’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 Braze dashboard (pictured below) as well as within our User API.


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.


Note: Enabling background mode for remote notifications is required for Braze’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:.

iOS Silent Notifications Limitations

The iOS operating system may gate notifications for some features. Please note that if you are experiencing difficulties with these features, the iOS’s silent notifications gate might be the cause.

Braze has several features which rely on iOS Silent Push Notifications:

Feature User Experience
Uninstall Tracking User receives a silent, nightly uninstall tracking push.
Geofences Silent syncing of geofences from server to device.
Push Stories User receives a push story.

For more information, check out Apple’s developer site on the Instance Method and Unreceived Notifications.

Advanced Settings

When creating push engagement, on the “Compose” step, you can select the “gear” icon to view the Advanced Settings available.

Advanced Settings

Alert Options

By clicking the checkbox here, you will notice a dropdown of key values available for adjusting how the notification will appear on devices.

Adding Content-Available Flag

The content-available flag instructs iOS 7+ devices to download new content in the background. Most commonly, this can be checked on should you be interested in sending silent notifications.

Adding Mutable-Content Flag

The mutable-content flag enables advanced receiver customizations in iOS 10+ devices. This flag will automatically be sent when composing a rich notification, regardless of the value of this checkbox.


Here you can enter a path to a sound file in your app bundle to specify a sound to be played when the push message is received. If the specified sound file does not exist or should the keyword ‘default’ be entered, Braze will use the default device alert sound.

Collapse ID

Specify a Collapse ID to coalesce similar notifications. If you send multiple notifications with the same Collapse ID, the device will only show the most recently received notification. For more information please find Apple’s documentation here.


Clicking the checkbox here will offer the option to set an expiration time for your message. Should a user’s device lose connectivity, Braze will continue to try and send the message until the specified time. If this is not set, the platform will default to an expiration of 30 days. Please note that push notifications that expire before delivery are not considered as failed and will not be recorded as a bounce.

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 Braze 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

Braze 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 Braze’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"
      withAppboyOptions:@{ ABKMinimumTriggerTimeIntervalKey : @(5) }];

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

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, Braze 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 deprecated beforeInAppMessageDisplayed:withKeyboardIsUp: UI delegate method has NOT been implemented, and the keyboard is currently being displayed
  • The beforeInAppMessageDisplayed: delegate method or the deprecated beforeInAppMessageDisplayed:withKeyboardIsUp: UI delegate method returned ABKDisplayInAppMessageLater
Discarding In-App Messages

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

  • The beforeInAppMessageDisplayed: delegate method or the deprecated beforeInAppMessageDisplayed:withKeyboardIsUp: UI 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 display the top-most in-app message on the stack 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 controller delegate, if you have implemented one.

Real Time In-App Message Creation & Display

In-app messages can also be locally created within the app and displayed via Braze. This is particularly useful for displaying messages that you wish to trigger within the app in real-time. Braze 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];


All of Braze’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. Braze 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 a Braze 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 Braze provides.

Setting Delegates

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

In-App Message Controller Delegate

This delegate contains one method beforeInAppMessageDisplayed: and is part of our Core subspec.

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"
      withAppboyOptions:@{ ABKInAppMessageControllerDelegateKey : self }];
                       withAppboyOptions:[ ABKInAppMessageControllerDelegateKey : self ]])

In-App Message UI Delegate

This delegate can be used with our UI and InAppMessage subspecs.

Set this delegate by calling:

[[Appboy sharedInstance].inAppMessageController.inAppMessageUIController setInAppMessageUIDelegate:self];

See our in-app message sample app for an example.

Migrate from Version 3.2.3 and Earlier

To migrate from version 3.2.3 or earlier, change the code with your delegate methods to conform to the ABKInAppMessageUIDelegate protocol instead of the ABKInAppMessageControllerDelegate and set the delegate by calling:

[[Appboy sharedInstance].inAppMessageController.inAppMessageUIController setInAppMessageUIDelegate: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

You may alternatively set orientation on a per-message basis. To do this, set an in-app message delegate. Then, in the beforeInAppMessageDisplayed: delegate method or the deprecated beforeInAppMessageDisplayed:withKeyboardIsUp: UI delegate method, set the orientation property on the ABKInAppMessage. For 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;
func beforeInAppMessageDisplayed(inAppMessage: ABKInAppMessage!) -> ABKInAppMessageDisplayChoice

If you have only implemented the UI delegate, the following UI delegate method will be called instead:

- (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: delegate method to add in-app message display logic, customize in-app messages before Braze displays them, or opt-out of Braze’s in-app message display logic and UI entirely.

For an implementation example, see our In-App Message Sample Application.

Note: If you are using the Core subspec, this is the method where you should customize and display your in-app message, and then return ABKDiscardInAppMessage.

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: delegate method or the deprecated beforeInAppMessageDisplayed:withKeyboardIsUp: UI 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 Braze’s in-app message display by returning ABKDiscardInAppMessage in the beforeInAppMessageDisplayed: delegate method or the deprecated beforeInAppMessageDisplayed:withKeyboardIsUp: UI delegate method). 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 Braze server.
- (void) logInAppMessageImpression;
// Registers that a user has clicked on an in-app message with the Braze server.
- (void) logInAppMessageClicked;
// Registers that a user has viewed a in-app message with the Braze server.
func logInAppMessageImpression()
// Registers that a user has clicked on a in-app message with the Braze 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

Note: This method cannot be used when using the Core subspec.

The following UI delegate method is called when the in-app message is clicked, and 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

Note: These methods cannot be used when using the Core subspec.

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 Braze 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 Braze. Braze 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 UI delegate method is called every time an in-app message is offered to ABKInAppMessageViewController to allow the app would like to pass a custom view controller to Braze for in-app message display:

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

All of our in-app message view controllers are open-sourced. You can use subclasses or categories to customize display or behavior of in-app messages.

See the in-app message view controllers for more details.

Custom In-App Message Triggering

By default in-app messages are triggered by event types which are logged by the SDK. If you would like to trigger in-app messages by server-sent events you are also able to achieve this.

To enable this feature you would send a silent push to the device which allows the device to log a SDK based event. This SDK event would subsequently trigger the user-facing in-app message.

Step 1: Handle Silent Push and Key Value Pairs

When building against iOS 10+ we recommend you integrate the UserNotifications framework. If you use the UserNotifications framework, add the following code within the userNotificationCenter(_:willPresent:withCompletionHandler:) method:

- (void)handleExtrasFromPush:(UNNotification *)notification {
 NSLog(@"A push was received");
NSDictionary *userInfo = notification.request.content.userInfo;
 if (userInfo !=nil && userInfo[@"IS_SERVER_EVENT"] !=nil ) {
   // Here based on the extras key-value pair, you can run some custom code.
   [[Appboy sharedInstance] logCustomEvent:@"IAM Trigger" withProperties:@{@"campaign_name": userInfo[@"CAMPAIGN_NAME"]}];

This will be called when a notification is received whilst the application is in the foreground. When the silent push is received an SDK recorded event ‘IAM Trigger’ will be logged against the user profile.

Step 2: Create a Push Campaign

Create a silent push campaign which is triggered via the server sent event. For details on how to create a silent push campaign please review this section of our Academy.


The push campaign must include key value pair extras which indicate that this push campaign is sent with the intention to log an SDK custom event. This event will be used to trigger the in-app message:


The code within the userNotificationCenter(_:willPresent:withCompletionHandler:) method checks for key IS_SERVER_EVENT and will log an SDK custom event if this is present.

You are able to alter either the event name or event properties by sending the desired value within the key-value pair extras of the push payload. These extras can be used as the parameter of either the event name, or as an event property, when logging the custom event.

Step 3: Create an In-App Message Campaign

Create your user visible in-app message campaign from within Braze’s dashboard. This campaign should have an Action Based delivery, and be triggered from the custom event logged from within the userNotificationCenter(_:willPresent:withCompletionHandler:) method.

In the example below the specific in-app message to be trigger has been configured by sending the event property as part of the initial silent push.


Note: Due to a push message being used to to record an SDK logged custom event, Braze will need to store a push token for each user in order to enable this solution. For iOS users, Braze will only store a token from the point that a user has been served the OS’s push prompt. Prior to this the user will not be reachable using push and the above solution will not be possible.

Method Declarations

For additional information see the following header files:

Implementation Samples

See AppDelegate.m, ViewController.m and CustomInAppMessageViewController.m in the in-app message sample app.

In-App Message Templates

Custom App Store 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. Next,implement the following delegate method to disable the default App Store review message:

- (ABKInAppMessageDisplayChoice)beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage {
   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 an In-App Messaging campaign with the following:

  • add the key-value pair “Appstore Review” : “true”
  • set the “On Click Behavior” to “Deep Link Into App,” using the above deep link (e.g., {YOUR-APP-SCHEME}:appstore-review)

Apple limits App Store review prompts to a maximum of 3 times per year for each user, so the campaign should be rate-limited to three times per year per user.


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 Braze’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 Braze’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 view controller ABKNewsFeedViewController will display the Braze News Feed.

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

Note: The News Feed that is called by the default behavior of an in-app message 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 ABKInAppMessageUIController and implement the ABKInAppMessageUIDelegate delegate method onInAppMessageClicked:.

News Feed View Controller Integration Options

The News Feed can be integrated with 2 view controller contexts, either in code or via a storyboard implementation.

  • You can set the instance’s title and navigation items before pushing it into a navigation controller
ABKNewsFeedTableViewController *newsFeed = [ABKNewsFeedTableViewController getNavigationFeedViewController];
[self.navigationController pushViewController:newsFeed animated:YES];
  • 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
ABKNewsFeedViewController *newsFeed = [[ABKNewsFeedViewController alloc] init];
[self.navigationController presentViewController:newsFeed animated:YES completion:nil];

Note: The Stopwatch sample project contains examples of the view controllers.

Defining a News Feed Category

Instances of the Braze 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 Braze 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];

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 Braze 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 Braze to refresh the user’s News Feed in Appboy.h using - (void) requestFeedRefresh;. For example: objc [[Appboy sharedInstance] requestFeedRefresh];

For more information see the Appboy.h header file.

Customizing the News Feed

You can create your own News Feed interface by extending ABKNewsFeedTableViewController. You can customize all UI elements and News Feed behavior in this way.

For an example, see the News Feed sample app.

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
// 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:
  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 Braze, we recommend storing (deep copy) the card data locally, updating the data and displaying yourself. The cards are accessible via ABKFeedController.

Base Card Model

Braze 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 Braze
  • 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 Braze dashboard
  • updated (read only) - The property is the unix timestamp of the card’s latest update time from Braze 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 @””. 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 Braze 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 @””. 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 @””. 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 @””. 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 Braze for a particular card.
  • logCardClicked - Manually log a click to Braze for a particular card. The SDK will only log a card click when the card has the url property with a valid value. All subclasses 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.


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];


Session Tracking

The Braze SDK reports session data that is used by the Braze 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 Braze 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 Braze initialization method startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions.

// Sets the session timeout to 60 seconds
[Appboy startWithApiKey:@"YOUR-API_KEY"
      withAppboyOptions:@{ ABKSessionTimeoutKey : @(60) }];
// Sets the session timeout to 60 seconds
                       withAppboyOptions:[ ABKSessionTimeoutKey : 60 ]])

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

Note: The minimum value for sessionTimeoutInSeconds is 1 second.

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.


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, Braze 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 Braze 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

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"];

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, Braze will automatically make a data refresh request for the News Feed, slideup and other Braze 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 Braze 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 Braze 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

[[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

Braze 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.

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
  • language
  • homeCity
  • bio
  • phone
  • userID
  • avatarImageURL
  • twitterAccountIdentifier
  • gender

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

Assigning Custom User Attributes

Beyond the attributes above, Braze 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: Braze 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 Braze 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 Braze 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"];

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 Braze Academy for details.

Setting Email Subscriptions

[[Appboy sharedInstance].user setEmailNotificationSubscriptionType: ABKNotificationSubscriptionType]

Setting Push Notification Subscriptions

[[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 Braze 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.

Braze 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

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

[[Appboy sharedInstance] logPurchase:@"your product ID"
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
  • Please note that if the product identifier is empty, the purchase will not be logged to Braze.

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.

  • A quantity input must be in the range of [0, 100] for the SDK to log a purchase.
  • Methods without a quantity input will have a default quantity value of 1.
  • Methods with a quantity input have no default value and must receive a quantity input for the SDK to log a purchase.

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.

[[Appboy sharedInstance] logPurchase:@"your product ID"
atPrice:[[[NSDecimalNumber alloc] initWithString:@"0.99"] autorelease]
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.


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 Braze 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 Braze user profiles, you need to fetch the user’s data and pass it to Braze.

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 Braze

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

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 Braze, 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 Braze

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

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, Braze enables location tracking after the host application has gained permission from the user. Provided that users have opted into location tracking, Braze will log a single location for each user on session start.

Requesting Location Tracking Permissions

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

[[Appboy sharedInstance].locationManager allowRequestWhenInUseLocationPermission];

Logging A Single Location

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

[[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"
      withAppboyOptions:@{ ABKDisableAutomaticLocationCollectionKey : @(YES) }];
withAppboyOptions:[ ABKDisableAutomaticLocationCollectionKey : true ]])

Manually Enabling iOS Location Targeting

If you wish to use your own CLLocationManager instead of Braze’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 Braze’s methods to manually pass location tracking information along to Braze.

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 Braze 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 Braze

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 Braze’s automatic location tracking has been disabled (i.e., ABKDisableAutomaticLocationCollectionKey has been set to YES).

[[Appboy sharedInstance].user setLastKnownLocationWithLatitude:latitude

[[Appboy sharedInstance].user setLastKnownLocationWithLatitude:latitude

For more information, see ABKUser.h.

Implementation Examples

AppDelegate.m in the Stopwatch sample application shows how to authorize Braze 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 a Braze flag in the payload. For more information, see our Uninstall Tracking page on Braze 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 Braze background push

Braze 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 Braze’s uninstall tracking notifications.

Step 3: Test from the Dashboard

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

  1. On the Campaigns page, create a Push Notification campaign and 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 Braze’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 Braze Academy.

Disabling Data Collection

In order to comply with data privacy regulations, data tracking activity on the iOS SDK can be stopped entirely using the method disableSDK. This method will cause all network connections to be cancelled, and the Braze SDK will not pass any data to Braze’s servers. If you wish to resume data collection at a later point in time, you can use the requestEnableSDKOnNextAppRun method in the future to resume data collection.

Additionally, you can use the method wipeDataAndDisableForAppRun to fully clear all client-side data stored on the device.

Unless a user uninstalls all apps from a vendor on a given device, the next Braze SDK/app run after calling wipeDataAndDisableForAppRun() will result in our server re-identifying that user via their device identifier (IDFV). In order to fully remove all user data, you should combine a call to wipeDataAndDisableForAppRun with a request to delete data on the server via the Braze REST API.

Customer Feedback

The Customer Feedback module has been deprecated and is not available to new integrations.

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


iOS ActivityFeed+FeedBack

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

Our Feedback view controllers are open-source and available here. You have a great deal of flexibility in how you choose to display the view controllers. There are two different versions of the view controllers to accommodate different navigation structures.

ABKModalFeedbackViewController presents the view controller in a modal view that includes a navigation bar, “Cancel,” and “Send” buttons. You can integrate it either programmatically or in your storyboard.

ABKModalFeedbackViewController *modalFeedback = [[ABKModalFeedbackViewController alloc] init];
[self presentViewController:modalFeedback animated:YES completion:nil];
presentViewController(ABKModalFeedbackViewController.init(), animated:true, completion: nil)

ABKNavigationFeedbackViewController can be used in a navigation stack as a child of a UINavigationController. his Feedback view controller includes a “Send” button.

ABKNavigationFeedbackViewController *navFeedback = [[ABKNavigationFeedbackViewController alloc] init];
[navigationController pushViewController:navFeedback animated:YES];
navigationController.pushViewController(ABKNavigationFeedbackViewController.init(), animated: true)

Storyboard Integration

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

Sample Code

ABKNavigationFeedbackViewController and ABKModalFeedbackViewController are utilized in the Feedback Sample App. For further details see the ABKFeedBackViewController header files

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

One Button Integration - Modal View

Below are examples of how to integrate the Braze 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 News Feed view.

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


To add customized behavior across the life cycle of a Feedback view controller, we recommend that you create a category or a subclass of the FeedbackViewController you are using and override the corresponding methods for different stages of the view controller.

The methods are:

- (ABKFeedback *)appboyFeedbackFromMessage:(NSString *)message email:(NSString *)email isBug:(BOOL)isBug;
This method is for customizing the feedback object from user inputs. It replaces the old feedbackViewControllerBeforeFeedbackSent delegate method.
- (void)feedbackSent:(ABKFeedbackSentResult)feedbackSentResult;
This method is for custom handling after feedback is sent. It replaces the old feedbackViewControllerFeedbackSent delegate method.
- (IBAction)issueButtonTapped:(UIButton *)sender;
The touch up inside action for the issue button. The default behavior is to change the select state of the button.
- (IBAction)sendButtonTapped:(UIBarButtonItem *)sender;
The touch up inside action for the send button. The default behavior is to check the validation of the feedback object, show the spinner view, and send the feedback through the Braze SDK.

Manual Feedback Collection

The following method will allow you to pass Feedback to Braze from a form or field within your app. This is perfect for passing feedback from an existing UI element to Braze.

The SDK will call the completion handler after the feedback sends successfully or fails to send..

- (void)submitFeedback:(ABKFeedback *)feedback
 withCompletionHandler:(nullable void (^)(ABKFeedbackSentResult feedbackSentResult))completionHandler;
Appboy.sharedInstance()!.submitFeedback(feedback) { (feedbackSentResult) in
      print("Feedback sent: \(feedbackSentResult)")

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

[[Appboy sharedInstance] logFeedbackDisplayed];

Third Party Provider Integrations

Braze has easy integrations with both 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.

Push Story Setup

Note: The Push Story feature requires UNNotification Framework and iOS 10. The feature is only available from iOS SDK version 3.2.1.

Step 1: Enable Push In Your App

Please follow The Push notification Integration to enable push in your app.

Step 2: Adding the Notifiation Content Extension Target

In your app project, go to menu “File”->”New”->”Target…”, add a new “Notification Content Extension” target and activate it.

Add Content Extension

Xcode should generate a new target for you and create files automatically for you including: - NotificationViewController.h - NotificationViewController.m - MainInterface.storyboard

Step 3: Enable Capacities

The Background Mode in the Capabilities section of the main app target is required by the Push Story feature. After turn on the background modes, select “Background fetch” and “Remote Notification”.

Enable Background Mode

You also need to add Capability App Groups. If you haven’t had any app group in your app, go the the Capability of the main app target, turn on the App Groups and click the “+”. Use your App’s bundle ID to create the App Group. For example, if your app’s bundle ID is, you can name your App Group You need to turn on the App Groups for both the main app target and the content extention target.

Add App Groups

Step 4: Updating the Podfile

Add the following line to your Podfile:

target 'YourContentExtensionTarget' do
  pod 'Appboy-Push-Story', '~>3.0'

Step 5: Updating your Notification View Controller

In your NotificationViewController.h, add following lines to add new properites and import the header files:

#import <AppboyPushStory/AppboyPushStory.h>
@property (nonatomic) IBOutlet ABKStoriesView *storiesView;
@property (nonatomic) ABKStoriesViewDataSource *dataSource;

In your NotificationViewController.m, remove the default implementation and add following code:

@implementation NotificationViewController

- (void)didReceiveNotification:(UNNotification *)notification {
  self.dataSource = [[ABKStoriesViewDataSource alloc] initWithNotification:notification

- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response
                     completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion {
  UNNotificationContentExtensionResponseOption option = [self.dataSource didReceiveNotificationResponse:response];

- (void)viewWillDisappear:(BOOL)animated {
  [self.dataSource viewWillDisappear];
  [super viewWillDisappear:animated];


Step 6: Updating the Braze Integration in Your Main App

Add ABKPushStoryAppGroupKey in the appboyOption dictionary as following when you initialize Braze:

NSMutableDictionary *appboyOptions = [NSMutableDictionary dictionary];
appboyOptions[ABKPushStoryAppGroupKey] = @"YOUR_APP_GROUP";
[Appboy startWithApiKey:@"YOUR_APPBOY_API_KEY"

Advanced Use Cases



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:

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:


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 Braze’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 Braze 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 (Braze’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 Braze 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, Braze’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 Braze integration will satisfy ATS requirements if every link you use in an Braze 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:


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:


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 Braze 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.


Web View UI Customization

Braze iOS SDK v.2.30.0 open sources the ABKModalWebViewController class, which is used to display web URLs from the SDK.

You can declare a category for, or directly modify, the ABKModalWebViewController class to apply any UI customization to the web view. Please check the class’s .h file and .m file for more detail.

Linking Handling 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 Braze initialization, pass a delegate object to the ABKURLDelegateKey in the appboyOptions of startWithApiKey:inApplication:withAppboyOptions:. Braze 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>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [Appboy startWithApiKey:@"YOUR-API-KEY"
        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 ([[ lowercaseString] isEqualToString:@""]) {
    // Handle Universal Links sent by the Braze iOS SDK
    [self handleUniversalLink:url];
    return YES;
  // Let Braze 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 Braze’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
  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") {
  return true

Fine Network Traffic Control

Request Processing Policies

Braze 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 Braze SDK will automatically handle all server communication, including:
    • Flushing custom events and attributes data to Braze’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 Braze’s features, such as in-app messages.
  • To minimize server load, Braze performs periodic flushes of new user data every few seconds.
  • Data can be manually flushed to Braze’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 Braze’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 Braze 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 Braze (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.
  • Braze 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) {
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 Braze.


Localization is supported in version 2.5+ of the Braze iOS SDK. In addition to English, Braze supports 29 languages in our built-in SDK messages. These pertain to the default messages displayed in applications integrated with Braze, 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 Braze 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 Braze 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*/
"" = "Unable to Establish\n Network Connection";
"" = "Please try again later.";
/* Feedback Alert Invalid Email Messages */
"" = "Invalid Email Address";
"" = "Please enter a valid email address and try again.";
/*Feedback Alert Empty Feedback Labels*/
"" = "Empty Feedback Message";
"" = "Please enter a feedback message.";
/*Feedback Modal Context Labels*/
"" = "Feedback";
"" = "Cancel";
"" = "Send";
"" = "Message";
"" = "Required";
"" = "Reporting an Issue?";
"" = "Provide contact info from:";
"" = "Required";
"" = "or";
"" = "Email (Required)";
/*News Feed Default Labels*/
"" = "FREE";
"Appboy.feed.card.cross-promotion.price.format" = "$%.2f";
"Appboy.feed.done-button.title" = "Done";
"" = "We have no updates.\nPlease check again later.";
"" = "Connection Error";
"" = "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

Step 1: Cloning the Braze SDK

  1. Clone the Braze iOS SDK Github project:
# This command will clone both versions of the Braze SDK
$ git clone
  1. In Xcode, from the project navigator, select the destination project or group for Braze
  2. Navigate to File > Add Files to “Project_Name”
  3. Add the AppboyKit and AppboyUI folders 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 Braze’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 Braze’s UI features, in-app messages will not display. Trying to display Braze’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 Braze 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, Braze iOS SDK only supports 4.x version of SDWebImage. If you have to use SDWebImage version 3.x, please use Braze SDK version 2.25.0 or below.

  8. The FLAnimatedImage framework is required to display GIF images in Braze 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"

Note: Be sure to initialize Braze 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

Braze 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 Braze 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 Braze 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 %@",];
[[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 Braze packages. For access please create a support ticket or speak with your Braze Customer Success Manager. Learn more in the Braze Academy.

To support geofences for iOS:

  1. Your integration must support background push notifications.

  2. Braze location collection must not be disabled.

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

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 Braze Background Push

Braze 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 Braze’s geofence sync notifications.

Step 3: Add NSLocationAlwaysUsageDescription to your Info.plist

Add the key NSLocationAlwaysUsageDescription and NSLocationAlwaysAndWhenInUseUsageDescription to your info.plist with a String value that has a description of why your application needs to track location. Both keys are required by iOS 11. 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 Braze 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. Braze’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 Braze’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

Braze’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 Braze 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

Braze’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.