Initial SDK setup

Install using Cocoapods

If you want to use Cocoapods to manage your external dependencies, simply add the following line to your Podfile:

pod 'FollowAnalytics'
If you don't have a podfile yet, open a console and do a pod init in your project directory.

Then, you only need to update your cocoapods dependencies:

pod install

If you don't have a podfile yet, open a console and do a pod init in your project directory.

Using a local version of the Pod

If you'd rather have the file locally, download the SDK, copy the followanalytics/ directory located in the expanded archive to your Xcode project root directory and use the following line in your Podfile: pod 'FollowAnalytics ~> 5.6.0', :path => "followanalytics"

Initialize with your API key

Prepare your API key

Be sure to have your API key for this step of the configuration. If you are not sure where to find it, please reach out to your Customer Success Manager or message

To initialize the SDK, you need to call the configureWithId method on it, and pass it your API key.

To do so:

  1. Import the <FollowAnalytics/FAFollowApps.h>, in your AppDelegate.h file if you use Objective-C, or in your Bridging-Header.h file in Swift.

    What if I don't have a bridging header file?

    • Create a new Objective C file in your project (File->New->File[Objective C for iOS]).
    • Accept the prompt (agree) to create a bridging header file between Objective C and Swift.
    • Delete your newly created Objective C file but keep the bridging header file ${YOURPROJ}-Bridging-Header.h.
  2. Add the FollowAppsDelegate protocol to your AppDelegate, and configure the SDK using the configureWithId method:


// in AppDelegate.h
@interface AppDelegate : UIResponder <FAFollowAppsDelegate>

// in AppDelegate.m
[FAFollowApps configureWithId:@"API_KEY_STRING" sharedAppGroup:@"APP_GROUP" automatise:YES  debugStateOn:NO options:launchOptions];


class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {

FAFollowApps.configure(withId: "API_KEY_STRING", sharedAppGroup: "APP_GROUP", automatise: true, debugStateOn: false, options: launchOptions)

If you can't use the automated integration (bool to false), please refer to the bottom for additional documentation dedicated page.


  • When developing your app, use debugStateOn: true to avoid sending irrelevant logs to the production server, and to see debugging information in the XCode console
  • Before publishing the app, make sure the debug state is OFF (debugStateOn: false).

Define the URL scheme

To allow users to retrieve their device ID for campaign testing and tagging plan debugging, declare a URL Scheme in the info tab of your Xcode project using the bundleId of your app as URL Scheme:

URL Scheme

Make sure that the bundle ID of your app appears in the URL Schemes field as shown in the screenshot (where com.follow-apps.followme.inHouse needs to be replaced).

Once configured, your FA device ID can be obtained by opening the following URI: xxx://, where xxx shall be replaced by the application bundle identifier (com.follow-apps.followme.inHouse in the example above).

FA users can request, from the product interface, that an e-mail message containing this link be sent to their device to facilitate the operation.

Register for notifications

Unless your app code is already registered to send push notifications, simply add a call to registerForPush following the initialization line you added previously:


[FAFollowApps configureWithId:@"API_KEY_STRING" sharedAppGroup:@"APP_GROUP" automatise:YES  debugStateOn:NO options:launchOptions];
[FAFollowApps registerForPush];

FAFollowApps.configure(withId: "API_KEY_STRING", sharedAppGroup: "APP_GROUP", automatise: true, debugStateOn: false, options: launchOptions)

Then, to ensure FollowAnalytics can detect uninstalls and send Silent Push Notifications, enable the capability in the capabilities tab of your Xcode Project, by clicking the Remote Notifications box under Background Modes:

There is nothing else to do, FollowAnalytics will handle its own notifications and you will still receive yours, as long as you compile with the appropriate Push certificates, and provide FollowAnalytics with the required .pem file. To generate the pem push certificate required for FollowAnalytics, you can follow the Certificates section in this tutorial.

Conflict with Notification Categories

If you register interactive notification categories: In your AppDelegate implement : - (void)followAppsRegisterNotificationCategories;

And register your category inside the method.

If you want to get the status of the user preferences you can call this method: FAFollowApps.isRegisteredForPushNotifications()

Register for location

FollowAnalytics will only access the location if you give the permission to the SDK. If you are already using it and want to share it with FollowAnalytics you will have to call : FAFollowApps.enableGeoffencing().


The SDK has a Validator that will ensure that everything is properly configured.

When your app is running in debug mode, a popup is shown at launch with the details of what is properly configured, and what is not. This makes it easier for you to validate that the various steps were performed properly.

Crash reporting test

To be able to validate that the crash configuration is working properly, you will have to launch your app once without the debugger, i.e. manually on the device or from the simulator, without using the "run" button in Xcode.

Validator description

To know more about what the validator checks and how, please refer to its dedicated page.


Data sent in debug mode will not appear on dashboards

When your app runs in DEBUG mode or in a simulator, the data is automatically sent as development logs, and is therefore not processed to be shown on the main UI page. DEBUG logs can be checked using the method given in the FAQ section.

See related entry in FAQ for more information on debug & release modes.

Logging best practices

To ensure your tags are relevant and will end up empowering your team through FollowAnalytics, please read the Logging best practices entry in the FAQ section.

Events vs Attributes

The FollowAnalytics SDK allows you to tag both events and attributes. If you are unsure about the difference, please refer to the corresponding FAQ entry.

Tag events

Regular, native event logging

The SDK allows you to log events happening in your code. These are the two methods you can call on the SDK:


+ (BOOL)logEventWithName:(NSString *)name details:(id)details;
+ (BOOL)logErrorWithName:(NSString *)name details:(id)details;


FAFollowApps.logEventWithName(name: String!, details: AnyObject!)
FAFollowApps.logErrorWithName(name: String!, details: AnyObject!)

Use the name as the unique identifier of your tag, use the details section to add specific details, context. Events can be renamed later: the name that you give to your event here can be overridden in the FollowAnalytics UI.

The details field can either be a String or a Hash, so that you can associated multiple key-values for additional context.

For instance, logging the appearance of a view could be done the following ways:


[FAFollowApps logEventWithName:@"Product view" details:@"Product reference"];
[FAFollowApps logEventWithName:@"In App purchase completed" details:@"Full pack"];

NSDictionary *detail_dictionary = @{ key_1: obj_1, key_2 : obj_2, key_3 : obj_3};
[FAFollowApps logEventWithName:@"Example log name" details:detail_dictionary];


FAFollowApps.logEventWithName("product view", details: "Product reference")
FAFollowApps.logErrorWithName("In App purchase completed", details: "Full pack")

let detail_dictionary:NSDictionary = [key_1: obj_1, key_2: obj_2, key_3: obj_3]
FAFollowApps.logEventWithName("Example log name", details: detail_dictionary)

In debug mode, the SDK will acknowledge the saving by writing in the console whatever it receives.

If the parameters sent to these methods are over 60Kb, the method will refuse them, return NO, and write a message in the console if it is running in debug mode.

Logging from a web view

If you happen to use a web view to display elements of your app, you can also tag events and errors from within your HTML/JS code. Our solution integrates itself inside a WKWebView. Here you have a selection of JavaScript methods that you can use:


FollowAnalytics.logError('err1','str value')
FollowAnalytics.logEvent('event1','str value')

The complete list of JavaScript methods is documented in FAWKWebViewJSBridge.h .

In order to use it, you need to do the following:


// Setup: Adopt WKUIDelegate protocol in your object of choice, typicaly a UIViewController.
// The rest of the steps asume that you are in a UIViewController context.
@interface ViewController <WKUIDelegate>
// ...

// Step 1: At the initialization of the hosting view controller, keep a strong reference to our FAWKWebViewJSBridge:
self.webViewJSBridge = [[FAWKWebViewJSBridge alloc] init];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
// Set up your configuration as needed.
// ...
// Step 2: give us your configuration in order for us to do our side of the setup
[self.webViewJSBridge addToConfiguration:configuration];
// Step 3: make sure that you use the configuration for the WKWebView
WKWebView* webView = [[WKWebView alloc] initWithFrame:frame configuration:configuration];
// Step 4: set your controller as a UIDelegate
webView.UIDelegate = self;

// Step 5: Implement the delegate method
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{
if([prompt hasPrefix:@"FollowAnalytics"]){
  [self.webViewJSBridge webView:webView runJavaScriptTextInputPanelWithPrompt:prompt defaultText:defaultText initiatedByFrame:frame completionHandler:completionHandler];
// Step 6(Optional): If you are using JavaScript method prompt in your JavaScript code, make sure that you implememnt a alternative
// for such cases, for example by using a UIAlertView
Legacy FAWebView :

Our legacy FAWebView still works. The documentation for the previous version covers that case.

User ID and attributes

You don't need a user ID to set attributes

Attributes are tied to the device when no user ID is provided. If a user ID is set, a profile is created and can be shared across apps.

In both cases, attributes can be used in segments and campaigns to target users.

User ID

If users can sign in somewhere in your app, you can specify their identifier to the SDK. This way, you will be able to relate crashes to specific users, link your users between FollowAnalytics and your CRM, and more.

This identifier is usually an e-mail address, client identifier, phone number, or anything else that uniquely ties your customers to you.

To register the user identifier, use:


+ (void)setUserId:(NSString *)userId;


func setUserId(userId: AnyObject!)

If you want to remove the user identifier (in case of a sign out for instance) use the following method:


+ (void)unsetCurrentUserIdentifier;

func unsetCurrentUserIdentifier

Predefined attributes

The SDK allows to set values for both custom and predefined attributes.

For predefined attributes, the SDK has the following properties:

+ (void)setUserFirstName:(NSString *)firstName;
+ (void)setUserLastName:(NSString *)lastName;
+ (void)setUserEmail:(NSString *)email;
+ (void)setUserDateBirth:(NSDate *)dateBirth;
+ (void)setUserGender:(FAGender)gender;
+ (void)setUserCountry:(NSString *)country;
+ (void)setUserCity:(NSString *)city;
+ (void)setUserRegion:(NSString *)region;
+ (void)setUserProfilePictureUrl:(NSString *)url;

They are "predefined" in the sense that they will be attached to default fields on your user profiles.

For example, to set user Joe's city to "Paris", you would proceed as follows:

[FAFollowApps setUserFirstName:@"Joe"];
[FAFollowApps setUserCity:@"Paris"];

Custom attributes

Double check your custom attribute types

When a value for an unknown attribute is received by the server, the attribute is declared with the type of that first value.

If you change the type of an attribute in the SDK, values might be refused server-side. Please ensure the types match by visiting the profile data section of the product.

Set a custom attribute

To set your custom attributes, you can use methods that are adapted for each type:

+ (void)setInt:(NSInteger)intValue forKey:(NSString *)key;
+ (void)setDouble:(double)doubleValue forKey:(NSString *)key;
+ (void)setBoolean:(BOOL)booleanValue forKey:(NSString *)key;
+ (void)setDate:(NSDate *)dateValue forKey:(NSString *)key;
+ (void)setDateTime:(NSDate *)dateValue forKey:(NSString *)key;
+ (void)setString:(NSString *)stringValue forKey:(NSString *)key;

For example, to set the user's job:

[FAFollowApps setString:@"Taxi Driver" forKey:@"key_job"];
Delete a custom attribute value

You can delete the value of an attribute using its key. For example, to delete the user's job:

[FAFollowApps removeCustomUserAttributeForKey:@"key_job"];
Set of Attributes

You can add or remove an item to or from a set of attributes.

To add an item:

NSSet *set = [[NSSet alloc] initWithObjects:@"apple", @"strawberry", @"lemon", nil];
[FAFollowApps addCustomUserAttributeSet:set forKey:@"fruits"];
To remove an item:

[FAFollowApps removeCustomUserAttributeSet:@"lemon" forKey:@"fruits"]; // Removes "lemon" from set of fruits.
And to clear a set:

[FAFollowApps deleteCustomUserAttributeSetForKey:@"fruits"]; // Removes all the items from the set.
For further information, refer to the SDK header file.

Opt-Out Analytics

The SDK can be configured to no longer gather and track analytics. It works in a opt-in by default mode, where one can chose later to opt-out. Once opted-out no further analytics events are collected. Already collected analytics - before being opted-out - are still sent to FollowAnalytics as usual. The opt-out state is persisted between restarts.

Once opted-out the following events are neither collected nor sent:

None of the data is stored in the phone. The final outcome is that no new analytics sessions are generated and sent to the server.

The rest of the product features should continue to work as usual (eg. push notifications registration, campaigns etc.) currently with the following limitations:

To inspect and set the opt-out state use the following property on FAFollowApps:

@property (class, nonatomic, readwrite, assign) BOOL optInAnalytics;
Please see additional doc in the new FollowAnalytics interface for more fine grained control.


You can record when the user expresses his demand to access or delete his personal data by calling one of the following methods:

[FollowAnalytics.GDPR requestToAccessMyData]
[FollowAnalytics.GDPR requestToDeleteMyData]

The SDK will record all requests and send them to our servers as soon as network conditions allow it. The SDK remembers pending requests between app restarts.


If you are using Datawallet, you need to enable it at startup. The FollowAnalyticsConfiguration object contains the property isDataWalletEnabled which needs to be set to true.

When Datawallet is enable the SDK will automatically download new policies as they are published. In order to deal with the currently known policy you can call the following APIs:

// gets the current policy
id<FADataWalletPolicy> policy = [FollowAnalytics.dataWallet getPolicy];
// true if the current policy is considered accepted
BOOL policyIsRead =  FollowAnalytics.dataWallet.isRead;
// informs the SDK that the user has accepted the current policy
[FollowAnalytics.dataWallet setIsRead:true];

When a new policy becomes available with a different major version from the current one the SDK will call on the main thread onDataWalletPolicyChange callback set on the FollowAnalyticsConfiguration. This is how you can set it up before starting the SDK:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration configurationWith:^(FollowAnalyticsMutableConfiguration * _Nonnull c) {
    c.isDataWalletEnabled = true;
    c.onDataWalletPolicyChange = ^{
      [self displayNewPolicy];

When the user accepts a policy call [FollowAnalytics.dataWallet setIsRead:true] to mark the current policy as read. The SDK records the major version of the current policy for future reference. When the current policy is accepted FollowAnalytics.dataWallet.isRead becomes true.

Whenever FollowAnalytics.dataWallet.isRead is false, it means that the current policy major version has yet to be accepted by the user.

By default, if no other configuration is present, the SDK will return a default policy with version 0.0. This policy is always read. You should check for version 0.0 and handle it appropriately.

If you want to provide a custom default policy for your users, download the policy json file from the back office and add it to your app, then modify the configuration to use it as follow :

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration configurationWith:^(FollowAnalyticsMutableConfiguration * _Nonnull c) {
// assuming that you have Policy.json in you main bundle
c.dataWalletDefaultPolicyPath = [[NSBundle mainBundle] pathForResource:@"Policy" ofType:@"json"];

Advanced Use Cases

Deep-linking: URL, Parameters

Campaigns created through FollowAnalytics allow to deep link to content in your app. You can either use an App Link, or use key-value parameters that are forwarded to your code.

Version 4.1.0 of the SDK introduced the possibility to use direct App Links like twitter://messages, which will send you to the corresponding screen inside the Twitter application.

You're able to access the functionality by enabling the Deep Linking switch in our UI, when creating a campaign. There you'll find the field App Link that expects you to enter these type of url schemas. It can either be an URL Schema to an external application or for your own application.

In the case of an external application you'll be presented with a system alert asking you to allow the redirection for the target application (the first time only).

If you need to handle the execution of the App Link yourself, you can implement this delegate and call the completionOpenUrl() when your app is ready to execute the App links. ⚠️This delegate is called only when the appLink is called from a notification.


- (void)followNotificationHandleOpenURLWithCompletion:(void (^_Nonnull)(void))completionOpenUrl`


func followNotificationHandleOpenURL(completion completionOpenUrl: @escaping () -> Void)

Deep-linking parameters

In FollowAnalytics campaigns, you can specify deep-linking parameters, e.g. in your push messages or for in-app button actions.

These parameters are given to the developer's code by the SDK. It is then up to the developer to implement the deep-linking in the app (specific path of screens, format of the arguments, etc.).

The parameters are passed using the following method of the FAFollowappsDelegate delegate:


- (void)followAppsShouldHandleParameters:(NSDictionary *)customParameters actionIdentifier:(NSString *)actionIdentifier actionTitle:(NSString *)actionTitle completionHandler:(void (^)())completionHandler;

func followAppsShouldHandleParameters(customParameters: [NSObject : AnyObject]!, actionIdentifier: String!, actionTitle: String!, completionHandler: (() -> Void)!)

Through the key value format, FollowAnalytics supports both standardized deep-linking (by defining the key you'll always use to give the path), and more direct parameter passing for simpler use cases integrations.

Regular deep-linking is usually implemented using a Router, that will handle the URL called on the app and translate it into the display of the right page, with the right content. An example of an open-source deeplinking router is DeepLinkKit.

Once this Router is configured in your app, the link with the FollowAnalytics SDK can be done the following way, supposing you decide on using the key deeplinking-path to pass your deeplink when creating your campaigns:


- (void)followAppsShouldHandleParameters:(NSDictionary *)customParameters actionIdentifier:(NSString *)actionIdentifier actionTitle:(NSString *)actionTitle completionHandler:(void (^)())completionHandler
    NSURL *url = [NSURL URLWithString:customParameters[@"deeplinking-path"]];
    if (url)
        [self.router handleURL:url withCompletion:nil];


func followAppsShouldHandleParameters(customParameters: [NSObject : AnyObject]!, actionIdentifier: String!, actionTitle: String!, completionHandler: (() -> Void)!) {
    let url:NSURL = NSURL(string: customParameters["deeplinking-path"] as! String)!
    self.router?.handleURL(url, withCompletion: nil)


The SDK has a feature called AdaptiveSDK: it allows to benefit from tags already in place and use them directly without having to implement a new tagging plan.

The SDK is currently compatible with Segment, Mixpanel, Google Analytics, Localytics and Urban Airship.

To check that our SDK detects the current integration you have in place, use:


[FAFollowApps detectedSDKs];



You can then pick the SDKs you want to get tags from:

For instance, to fetch tags from Mixpanel, you would use:


[FAFollowApps fetchTagsFromSDKs:FASDKMixpanelKey];



If you wish to fetch tags from all compatible SDKs, you can chain the 2 method calls:


[FAFollowApps fetchTagsFromSDKs:[FAFollowApps detectedSDKs]];


How it works

All the events you save using the compatible Analytics SDKs will also be saved on FollowAnalytics. The event name will be prefixed with a two-character code identifying the source: UA for UrbanAirship, LL for Localytics, and so on.

Just like for events tagged directly with our SDK, you can see logs in your developer console when a log is saved, so that you can check it is properly working.

Rich iOS 10 notifications

Notification Service Extension Framework


To make your app able to receive iOS 10 rich notifications & badge incrementation, follow these steps:

  1. In your xCode project, add a new target.

  2. Select Notification Service Extension, give it a name and confirm. Then when prompted, activate the scheme.

Install using Cocoapods
  1. If you want to use Cocoapods to manage your external dependencies, simply add the following line to your Podfile only in the extension target:
pod 'FANotificationExtension'

If you don't have a podfile yet, open a console and do a pod init in your project directory.

Then, you only need to update your cocoapods dependencies:

pod install
  1. Select the target of your main app, in Build phase

    1. Press the + button, and select New Copy File Phase.
    2. Change the destination to Frameworks
    3. Add the FANotificationExtension.framework, you should find it inside the Pod folder.
  2. Still in the Build phase

    1. Press the + button, and select New Run Script Phase.
    2. Add this script: bash "${PODS_ROOT}/FANotificationExtension/FANotificationExtension.framework/"
Manual installation
  1. Drag and drop the framework in your project and add it to your extension target.
  2. Go to your project configuration, select your target(the extension). Under the General pane, add the following frameworks (if not already listed) in the Linked Frameworks and Libraries section:

    • UserNotifications.framework
    • MobileCoreServices.framework
  3. Select the target of your main app, in Build phase

    1. Press the + button, and select New Copy File Phase.
    2. Change the destination to Frameworks
    3. Add the FANotificationService.framework
  4. Create a new Run Script Phase in your app’s target’s Build Phases and paste the following snippet in the script text field:

bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/FANotificationExtension.framework/"
This step is required to work around an [App Store submission bug]( when archiving universal binaries.
  1. import the <FANotificationExtension/FANotificationExtension.h> in your file of your target extension.
  2. Implement the code in the method: didReceiveNotificationRequest:


- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];

    [FANotificationService getFAContentIfNeededWithRequest:request bestContent:self.bestAttemptContent appGroup:@"APPGROUP" completion:^(UNMutableNotificationContent * _Nullable newContent) {

    // Modify the notification content here...



override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    self.contentHandler = contentHandler
    bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
    if let bestAttemptContent = bestAttemptContent {
            FANotificationService.getFAContentIfNeeded(with: request, bestContent: bestAttemptContent, appGroup: "APPGROUP", completion: { (newContent:UNMutableNotificationContent?) in
            // Modify the notification content here...


⚠️If you need to inscrease the badge number with a push, you will need an app Group between your main App and you extension.

Badge management

For using this feature, there are 3 requirements: * Have the Notification Service Extension with our FollowAnalytics extension framework. more information here * Create an app group between your app and the extension. * Specify your app group in the init (configureWithId...)

Just after initialize follow call :


[FABadge enable];



We only update the value of the badge when a push is receive. You have to implement the logic and the code to decrement or reset the value. They are 3 methods:


[FABadge setBadge:INTEGER]; // Set the value of the icon badge number


FABadge.setBadge(Int) // Set the value of the icon badge number


[FABadge updateBadgeBy:INTEGER]; // Update the value of the icon badge number


FABadge.updateBadgeBy(Int) // Update the value of the icon badge number


[FABadge badge]; // Get the value of the icon badge number


FABadge.badge // Get the value of the icon badge number

Control over campaigns

Custom handling of rich campaigns

Rich campaigns can be handled directly by the application code, instead of being showed automatically by the SDK. The behavior is defined when creating the campaign, using the "automatically display content" switch.

For campaigns where the content is not handled by FollowAnalytics, implement the following FAFollowAppsDelegate method in your AppDelegate:


- (void)followAppsShouldHandleWebViewUrl:(NSURL *)url withTitle:(NSString *)webviewTitle;


func followAppsShouldHandleWebViewUrl(url: NSURL!, withTitle webviewTitle: String!)

Pausing in-app campaigns

You can prevent in-app campaigns from being displayed on certain views of your app. This can be useful when you are displaying a media content, or if the topmost screen is only shown for a few seconds, for instance.

Any campaign supposed to be displayed when the mechanism is paused is stacked and shown as soon as it is resumed.

To tell the SDK to prevent the display of rich campaigns, and then to activate it back, use the following methods:


[FAFollowApps pauseCampaignDisplay]
[FAFollowApps resumeCampaignDisplay]



Tip: use view lifecycle methods

If you want to prevent the display on a given page, you can call the pause method from the viewDidAppear method, and the resume one in the viewDidDisappear call.

Tip: only allow display in some places of the app

You can use these methods the other way round, by pausing all campaigns when the app starts, right after the SDK was initialized, and then resuming where the messages can be shown. Just pause again when the user leaves that "safe" area of your app.

Interactive notifications

To ensure your interactive notification categories are properly set, write your category definition code inside the followAppsRegisterNotificationCategories delegate method, so that the SDK can trigger this registration when needed. It will be called when the SDK registers for push notifications.

This allows to use custom button and actions on your notifications.

This has to be done in your code. The categories created can then be used when creating a FollowAnalytics push campaign.

If a user receives an interactive notification sent from FollowAnalytics, and taps one of the buttons, it calls the followAppsShouldHandleParameters:actionIdentifier:actionTitle:completionHandler: method and passes the action identifier and title. If no custom parameter was defined when creating the campaign, the first parameter is an empty dictionary.

The completionHandler comes from the default interactive notification handling methods. Make sure you call it if it is not nil as it won't be called by FA if passed to you.

To register actions, you can proceed as follows:


- (void)followAppsRegisterNotificationCategories {
    UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
    category.identifier = @"categoryIdentifier1";
[category setActions:actionsArray forContext:UIUserNotificationActionContextDefault];
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:[NSSet setWithObjects:category, nil]];
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];


func followAppsRegisterNotificationCategories() {
  let category:UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
  category.identifier = "categoryIdentifier1"
  category.setActions(actionsArray, forContext:UIUserNotificationActionContext.Default)
  let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: Set(arrayLiteral: category))

Get params from last notification

Some applications might not be able to receive the notification custom parameters. Although this is unlikely, you can get the latest NSDictionary containing the custom parameters passed by the notification by calling:

[FAFollowApps lastPushCampaignParams]

You cannot call this method twice

This is a one shot call as a second call to this method will return an empty NSDictionary.

Campaign Archives

All scheduled campaigns received by a device can be archived locally and accessed from the developer's code, formatted as a message object. In order to configure your campaign storage, you have to call this method:


[FAFollowApps storeMessagesPush:YES inApp:YES];

FAFollowApps.storeMessagesPush(true, inApp: true)
We recommend adding it right after the FollowAnalytics SDK initialization.

In the class : FAInApp & FAPush you will find all methods related to a simple database.

⚠️ If you use this feature (storeMessages), we won't delete any message.

InApp Template Configuration

In your info.plist add the following line (only required for fullScreen InApp): View controller-based status bar appearance and set the boolean to NO

You can stop the display of InApp Campaigns in screens where you don't want them to appear. In order to do this call theses methods


[FAFollowApps pausePopupCampaignDisplay];
[FAFollowApps pauseBannerCampaignDisplay];


Whenever you want to display them, just call these other methods


[FAFollowApps resumePopupCampaignDisplay]
[FAFollowApps resumeBannerCampaignDisplay]




The embeddedView is very particular, it will move contents in your view. That is why to use one, you will need to activate autoLayout and use constraints. Otherwise you will need to move your affected content manually.



FAEmbeddedView *embeddedView = [[FAEmbeddedView alloc] init];
FAEmbeddedView *embeddedView = [[FAEmbeddedView alloc] initWithCategory:@"pub"]; //If you have more than one embedded in your app, use the category to identify it
FAEmbeddedView *embeddedView = [[FAEmbeddedView alloc] initWithMaxHeight:100]; // If your view don't have a scrollView or the height of view can't overpass a precise height, you can specify a maximum height for the embedded.
FAEmbeddedView *embeddedView = [[FAEmbeddedView alloc] initWithCategory:@"pub" maxHeight:100];


 let embeddedView = FAEmbeddedView()
 let embeddedView = FAEmbeddedView(category: "pub")
 let embeddedView = FAEmbeddedView(maxHeight: 100)
 let embeddedView = FAEmbeddedView(category: "pub", maxHeight: 100)
add the delegate in your controller: <FAEmbeddedViewDelegate> and set the delegate:


embeddedView.embeddedDelegate = self;


embeddedView.embeddedDelegate = self
Add the FAEmbeddedView in the container created in Interface Builder:


[self.myEmbeddedViewContainer addSubView:embeddedView];

You can implement these methods if needed:


- (BOOL)embeddedViewWillBeDisplayed:(FAEmbeddedView *)embeddedView;
- (NSInteger)embeddedView:(FAEmbeddedView *)embeddedView didFinishLoadWithpreferedHeight:(NSInteger)preferedHeight;

func embeddedViewWillBeDisplayed(_ embeddedView: FAEmbeddedView!) -> Bool
func embeddedView(_ embeddedView: FAEmbeddedView!, didFinishLoadWithpreferedHeight preferedHeight: Int) -> Int


There are 3 methods you can call to have a preview of the embedded. Theses methods are only here for testing and debugging. You will have a preview of your view if the embedded is activated.

Initialize the embedded (don't forget the first step in your interface view).


FAEmbeddedView *embeddedView = [[FAEmbeddedView alloc] init];
[self.myEmbeddedViewContainer addSubView:embeddedView];

let embeddedView = FAEmbeddedView()

And after you just have to call one of theses methods:


FAEmbeddedView *embeddedView = [FAEmbeddedView displayBigForDebuggingWithMaxheight:INTEGER] // Display a embedded with a big height (around 600)
FAEmbeddedView *embeddedView = [FAEmbeddedView displayRegularForDebuggingWithMaxheight:INTEGER]; // Display a embedded with a regular height (around 400)
FAEmbeddedView *embeddedView = [FAEmbeddedView displaySmallForDebuggingWithMaxheight:INTEGER]; // Display a embedded with a small height (around 200)
[YOUR_VIEW addSubview:embeddedView]


let embeddedView = FAEmbeddedView.displayBigForDebugging(withMaxHeight : Int) // Display an embedded view with a height of ~600px
let embeddedView = FAEmbeddedView.displayRegularForDebugging(withMaxHeight : Int) // Display an embedded view with a height of ~400px
let embeddedView = FAEmbeddedView.displaySmallForDebugging(withMaxHeight : Int) // Display an embedded view with a height of ~200px


Apple Watch setup


Add the watchConnectivity.framework library to your watch and app projects.

Drag and drop the FAWatchKit2SDK.framework in the Frameworks group in the XCode project navigator (left pane) and check both the Copy boxes. Select the build scheme watch Extension, as shown below.


[FAWatchKit2SDK configureForMainInterfaceController:self];
* Add the following code outside the self block of the awakeWithContext: method, in the NotificationController.m file:

[FAWatchKit2SDK configureForNotificationInterfaceController:self];
* If you defined a glance for you watch app, import the <FAWatchKit2SDK/FAWatchKit2SDK.h> header file in your GlanceController, and add the following code as the first line of the awakeWithContext: method:

[FAWatchKit2SDK configureForGlanceInterfaceController:self];
Then you can log events and errors the way you do it with the main SDK:

+ (BOOL)logEventWithName:(NSString *)name details:(id)details;
+ (BOOL)logErrorWithName:(NSString *)name details:(id)details;

Manual install

Cocoapods is the prefered method

Even though you can install the SDK manually, cocoapods is the prefered method, since it will take care of much of the work and ensure your SDK is easily updated.

  1. Go to your Xcode project’s General settings of your target, drag and drop the FollowAnalytics.framework in the Embedded Binaries section. Make sure Copy items if needed is selected and click Finish.

  2. Create a new Run Script Phase in your app’s target’s Build Phases and paste the following snippet in the script text field:

bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/FollowAnalytics.framework/"
This step is required to work around an [App Store submission bug]( when archiving universal binaries.
  1. Now, you can proceed to configuring and validating your setup.

Manual integration

In this integration you will have to listen to our delegate followAppsDelegate, a couple of methods of your AppDelegate and UNUserNotificationCenter to forward them to us. This is needed so we can track the UIApplication life cycle (EnterBackground, EnterForeground, …).

For example :

    func applicationDidEnterBackground(_ application: UIApplication) {

You will find here a sample project to show you how to do the non-automatic integration with all methods require.


Access your original Swift AppDelegate

If you need to access your application original delegate you can now use:


Handling URLs in your application

Apple introduced App Transport Security (ATS) in iOS 9 to improve user security and privacy by requiring apps to use secure network connections over HTTPS. At WWDC 2016 they announced that apps submitted to the App Store will be required to support ATS at the end of the year. To give you additional time to prepare, this deadline was extended. Apple has yet to announce the new date. Learn more about it here.

While this security policy is not enforced you can load unsafe urls by adding the following exception to your .plist.


When creating campaigns in FollowAnalytics, you will be able to use URLs for message contents or actions. We recommend using HTTPS, but if you cannot, you will need to add this exception in your .plist file.