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 'FollowApps'

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 FollowApps/ directory located in Pod/ to your Xcode project root directory and use the following line in your Podfile: pod 'FollowApps', :path => "FollowApps"

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 <FollowApps/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 refere to the bottom for additional documentation dedicated page.

    The switch between debug and release mode is not automatic in Swift

    • 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

If your app code is already registered to send push notifications, nothing to do here. Otherwise, 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, activate the Silent Notifications capabilities 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()

⚠️ If you call it in the didFinishLaunchingWithOptions it will directly trigger the popup asking for the location. We advise you to only call it after you ask the permission to the user.


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. To do so:

  1. Make sure you use the FAWebView, both in your .h file and your .xib or Storyboard, instead of the regular UIWebView. For the latter, you need to set the Custom Class parameter of your UIWebView to FAWebView in the Identity Inspector.

  2. Use the provided FAWebView.js in your web page code to save events. For instance, you could do:

    <a href="#" onclick="FALogEvent('My event', 'My event details')">Log an event</a>
    <a href="#" onclick="FALogError('My error', '')">Log an error</a>
    <a href="#" onclick="FALogEvent('My event', {'hello': 'Hi', 'How are you': 'good!'})">Log an event with hash</a>
  3. In case you want to register for push through a webview, you need to call FARegisterForPush(), like for instance: <a href="#" onclick="FARegisterForPush()">register for push</a>

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.

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. Presse the + button, and select New Copy File Phase.
    2. Change the destination to Frameworks
    3. Add the FANotificationExtension.framework, you should find it in the Pod folder.
  2. Still in the Build phase

    1. Presse 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. Presse the + button, and select New Copy File Phase.
    2. Change the destination to Frameworks
    3. Add the FANotificationService.framework
  4. Select the Build phase

    1. Presse the + button, and select New Run Script Phase.
    2. Add this script: bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/FANotificationExtension.framework/"
  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 initilize 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 needded:


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

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


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. Drag and drop the FollowApps.framework in the Frameworks group in the Xcode project navigator (left pane) and check both the Copy box and your target's.

  2. Go to your project configuration, select your target. Under the General pane, add the following frameworks (if not already listed) in the Linked Frameworks and Libraries section:

    • Security.framework
    • SystemConfiguration.framework
    • CoreTelephony.framework
    • CoreLocation.framework
    • libc++.dylib (before iOS 9) or libc++.tbd (iOS 9 and later)
    • libsqlite3.dylib (before iOS 9) or libsqlite3.tbd (iOS 9 and later)
    • UserNotifications.framework
    • libz.tbd
    • MobileCoreServices.framework
    • ZipArchive.framework (You can find it in the zip folder)

  3. Under the Build Settings pane, look for the Other Linker Flags entry, and add it the following value: -objC -lz -lc++ -lsqlite3

  4. To improve the accuracy of your crash reports on the platform, look for Strip style, and set it to Debugging Symbols

  5. 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 lyfe 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.




To allow your user to opt-out of data collection, drag and drop the Settings.bundle file in the Xcode project navigator. It'll add a settings pane in the settings app on their device, in which they will find a switch that they can turn off to disable logging.

If you already have a settings bundle, simply add the two items from the provided bundle into yours (in the same order).

Settings bundle localization

In order to add localizations for the Settings bundle the procedure is as follows:

As an example, the French localization of the file looks like this:

/* Localize the settings strings here if needed */
"Anonymous usage reports" = "Rapports d'Usage Anonymes";
"Help us improve the app by sending anonymous usage reports. Your data is not shared with third parties." =
"Aidez-nous à améliorer l'application en envoyant anonymement les rapports d'usage. Vos données ne seront pas partagées avec des tiers.";
"Send usage reports" = "Envoyer les rapports";

Access your original Swift AppDelegate

If you need to access your application's 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 folloing 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.

Migrating from older versions

Migrating from 5.0 to 5.1

Migrating from 4 to 5.0.0

To use version 5.0.0+ of the iOS SDK:

Migrating from 3.* to 4.*

The migration is seamless. However, please refer to the section around user attributes to learn how to feed user profiles using the SDK.

Migrating from 2.* to 3.0.0

To use version 3.0.0+ of the iOS SDK:

Nothing else changes. Please refer to the rest of the documentation to discover what the newer versions of the SDK now allow.