Prerequisites

Minimal version of iOS

The current version of the FollowAnalytics SDK works with iOS version 9.0 and above.

Before you start to integrate the SDK into your app, you need to register your app and generate its API key on the FollowAnalytics platform, by following the instructions here.

Getting Started

In this section, you will find the basic steps to integrate the SDK into your app:

Once the integration is finished, we highly recommend you test the setup. Then, you can start tagging events and saving attributes in your app.

Installation from CocoaPods

Starting with CocoaPods

The best way to install the SDK is with CocoaPods. This will allow you to easily update the SDK when new versions are released. If you have not installed CocoaPods yet, you may refer to this site, where you can install Cocoapods.

Before you start, be sure to have a Podfile. You could create one by writing pod init in your terminal in your project. To open your Podfile, you could find it in your Workspace, or by writing open -a Xcode Podfile in the terminal.

  1. Add pod 'FollowAnalytics','~> 6.5.4' in the Podfile (see screenshot below)

  2. Run pod repo update from the command line. This will enable CocoaPods to detect the latest available version of FollowAnalytics.

  3. Run pod install

Now FollowAnalytics is successfully installed.

Use the .xcworkspace file

To open your project with Xcode, use the .xcworkspace file generated by CocoaPods.

Manual installation

Cocoapods is the preferred method

Even though you can install the SDK manually, cocoapods is the preferred 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 Build Phases and paste the following snippet in the script text field:

    bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/FollowAnalytics.framework/strip-frameworks.sh"
    

    This step is required to work around an App Store submission bug when archiving universal binaries.

Initialize the SDK with your API key

Prerequisites: Generate your API key

If you haven't already, you can generate the API of your app by following the instructions here.

  1. Import the FollowAnalytics framework in your AppDelegate.

  2. Inside your UIApplicationDelegate implementation, create a configuration object and set the required fields

  3. Just below the configuration object, start the SDK

Your code should look like this:

#import <FollowAnalytics/FollowAnalytics.h>

@implementation AppDelegate
- (BOOL)application:(UIApplication*)application
  didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
  // ....
  // as early as possible
  FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
    configurationWith:^(FollowAnalyticsConfiguration* _Nonnull config) {
      config.apiKey = @"YOUR API KEY";
      config.isVerbose = true; // Before publishing the app, set isVerbose to false
    }];
  [FollowAnalytics startWithConfiguration:configuration startupOptions:launchOptions];
  //....
}
// ..
@end
import FollowAnalytics

class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions
    launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // ....
    let configuration = FollowAnalyticsConfiguration.init { (config) in
      config.apiKey = "YOUR API KEY"
      config.isVerbose = true // Before publishing the app, set isVerbose to false
    }
    FollowAnalytics.start(with: configuration, startupOptions: launchOptions)
    // ....
  }
}

You can configure the SDK with all the available parameters which you can find here.

Setting up push notifications

To set up push notification, you will need to perform the following steps:

We recommend to use p8 authentication key

We strongly recommend using a p8 authentication key as it is valid for all the apps added to your Apple developer account and doesn't have an expiration date. While a p12 certificate is only valid for a unique app and must be renewed each year to continue sending push notifications to your app.

Generate and import a p12 push certificate

  1. If you don't have a Certificate Signing Request yet, create one by following instructions here.

  2. Go to your Apple developer account in the Certificates, Identifiers & Profiles page.

  3. Click on the Identifiers tab.

  4. If you already have an app Identifier (App ID), click on your App ID, otherwise click on the + button and create a new App ID.

  5. In the list of Capabilities, check the Push Notifications box and click on Save.

  6. Click on the Certificates tab.

  7. Click on the + button to add a new certificate.

  8. Select Apple Push Notification service SSL (Sandbox & Production) and click on Continue.

  9. Select your App ID in the list and click on Continue.

  10. In the file picker, select the previously created Certificate Signing Request from your disk and click on Continue.

  11. Click on Download to retrieve the .cer certificate file.

  12. Double-click the downloaded certificate, this should open the Keychain Access app.

  13. Locate the certificate in Keychain Access under Certificates, right-click on it, select Export Apple Push Services:… and save it as a .p12 file.

  14. Enter a password for your certificate.

  15. Go to the Administration page of your app on the FollowAnalytics platform.

  16. Add the exported .p12 file and click on Proceed.

  17. Enter the certificate password and click on Proceed.

Do not forget to ensure your provisioning profile has the Push Notifications service enabled by expanding it from the list in your developer portal.

Generate and import a p8 authentication key

  1. Go to your Apple developer account in the Certificates, Identifiers & Profiles page.

  2. Click on the Keys tab.

  3. Click on the + button to add a new Key.

  4. Enter a name in the Key Name field.

  5. Check the Apple Push Notification service (APNs)

  6. Click on Continue and then on Register.

  7. Click on Download to retrieve the .p8 file. Note that once downloaded, you will not be able to download the p8 file again.

  8. Get back to the Keys list and click on the Identifiers tab.

  9. If you already have an app Identifier (App ID), click on your App ID, otherwise click on the + button and create a new App ID.

  10. In the list of Capabilities, check the Push Notifications box and and click on Save.

  11. Go to the Administration page of your app on the FollowAnalytics platform.

  12. Add the downloaded .p8 file and click on Proceed.

  13. Enter your Key ID and Issuer Key. Your Key ID can be found from the Keys tab of the Certificates, Identifiers & Profiles page in the Apple developer account. Your Issuer Key is your team ID, it can be found from the Membership page in the Apple developer account.

Do not forget to ensure your provisioning profile has the Push Notifications service enabled by expanding it from the list in your developer portal.

Push notification capabilities

In the Capabilities tab of your Xcode Project:

  1. Enable Push Notifications by setting the switch to ON.

  2. Enable the Remote notifications capability in the Background Modes:

    This will ensure that FollowAnalytics can detect uninstalls and send Silent Push Notifications.

Enable quiet notifications

Since iOS 12, Apple introduced a way to receive non-interruptive notifications before the user grants notification authorization. These are notification delivered quietly (no sound, no banner) and that are only displayed in the Notification Center.

You can allow your app to receive quiet notifications from FollowAnalytics as soon as it's installed on the user's device. To do so, just call requestProvisionalNotificationAuthorization in the didFinishLaunchingWithOptions method of your app delegate, after the SDK initialization call.

+ (void)requestProvisionalNotificationAuthorization;
static func requestProvisionalNotificationAuthorization()

Prompt the user to allow notifications

Later in the user experience, you can ask the user to provide its authorization for notifications. You can do this by calling the requestNotificationAuthorisation method at some point:

+ (void)requestNotificationAuthorization;
static func requestNotificationAuthorisation()

If you don't call and user hasn't allowed notifications, your app will only be able to receive silent notifications.

Enable device registration

Registering devices on iOS

Registering devices is enables users of the FollowAnalytics platform to test their campaigns. Unlike Android, you need to declare the URL scheme on your app on iOS so the users can use this feature.

To allow the user to retrieve the device ID for testing from the platform, declare a URL Scheme in the info tab of your Xcode project using the bundleId of your app as URL Scheme:

  1. Go to the info tab of the project targets.

  2. At the bottom you select the subsection called "URL Types".

  3. Click the + sign at the bottom.

  4. Add the bundle ID of your app in both identifier and URL Schemes fields

URL Scheme

Once configured, your FollowAnalytics device ID can be obtained by opening the following URI: YourURLScheme://followanalytics.com/deviceId, with YourURLScheme as the application bundle ID.

Now, users can add devices to the FollowAnalytics platform without knowing the device ID. From the platform, they will send an email a link that will open the app and allow them to register the device.

Setting up keychain access group

Keychain is a secure storage suitable for short bits of sensitive information and by default, the data saved in one application can not be read in other applications. To share the Device ID across applications of the same publisher it is required to specify a keychain group name. To configure it, you will need to perform the following steps:

Enable keychain sharing

  1. In XCode project settings, select your app target and from the Signing & Capabilities tab click +Capability;
  2. From the popup find and select the Keychain Sharing option;
  3. Back on the capabilities list of the application add the group name to be used, e.g. keychain.group.name.example.

Keychain Group Name

Setup the group name in the SDK

Set the configuration field .keychainGroupName with the respective publisher Team ID and Group Name joined but a ..

To know your Team ID:

  1. Login to your Apple developer account (https://developer.apple.com/account);
  2. Select Membership from the left panel;
  3. Find the Team ID value from the Membership Information list.
FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
    configurationWith:^(FollowAnalyticsConfiguration* _Nonnull config) {
      // ...
      // Replacing the YOUR_TEAM_ID with the respective value.
      config.keychainGroupName = @"YOUR_TEAM_ID.keychain.group.name.example";
      // ...
    }];
  let configuration = FollowAnalyticsConfiguration.init { (config) in
    // ...
    // Replacing the YOUR_TEAM_ID with the respective value.
    config.keychainGroupName = "YOUR_TEAM_ID.keychain.group.name.example"
    // ...
  }
  FollowAnalytics.start(with: configuration, startupOptions: launchOptions)

Analytics

To enrich User Analytics on the platform, FollowAnalytics SDK allows you to:

Logs and attributes can also be used to target specific audiences when creating campaigns on the platform. You can find more information about the difference between logs and attributes here.

Our CSM team can help you creating relevant logs in your app. You can also find our recommendations here.

Logs and attributes are ignored by the platform is some conditions

When FollowAnalyticsConfiguration.apiMode is set to Dev, the platform completely ignores data received by the SDK. As a result, data is not included in User Analytics and not usable in Audience targeting, but you can still see them in the Device Observer.

The SDK will automatically set apiMode to Dev in the following conditions:

  • The SDK is started with FollowAnalyticsConfiguration.isVerbose set to true.
  • Your app is running in a simulator.

Be careful to have FollowAnalyticsConfiguration.isVerbose set to false for your app release, otherwise all your app data will be ignored by the platform.

Logging Events and Errors

You can log Events and Errors in your app by calling the following methods:

+ (void)logEvent:(nonnull NSString*)name details:(nullable id)details;
+ (void)logError:(nonnull NSString*)name details:(nullable id)details;
static func logEvent(name: String, details: Any?)
static func logError(name: String, details: Any?)

Those methods print the log description in the console and also print an information about the log validity.

Use name as a unique identifier for your log. Use details to pass specific context to your log.

If details exceeds 60KB it will be considered as invalid. Additionally, to be valid, details must be one of the following:

Passing any invalid details to those methods will:

The following code shows an example of how you can log the viewing and the purchasing of a product:

[FollowAnalytics logEvent:@"Product view" details:@"RF1672"];

NSDictionary *details = @{@"reference": @"RF1672",
                          @"payment_mode" : @"credit_card",
                          @"color" : @"red"};
[FollowAnalytics logEvent:@"Product purchased" details:details];
FollowAnalytics.logEvent("Product view", details: "RF1672")

let details = ["reference": "RF1672",
               "payment_mode": "credit_card",
               "color": "red"]
FollowAnalytics.logEvent("Product purchased", details: details)

Logging Locations

There are two ways for logging locations:

+ (void)logLocationWithLatitude:(double)latitude longitude:(double)longitude;
+ (void)logLocation:(nonnull CLLocation*)location;
static func logLocation(withLatitude: Double, longitude: Double)
static func logLocation(location: CLLocation)

Those methods print the log description in the console and also print an information about the log validity:

One way to use the logLocation method, is to implement the didUpdateLocations method of the CLLocationManagerDelegate to log the last location update. FollowAnalytics SDK will take care of ignoring a location log if this location is too close from the last one:

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
  if let location = locations.last {
    FollowAnalytics.logLocation(location)
  }
}

Last known location logs

If you don't request location updates, and you don't send location logs you can still have location logged thanks to the configuration option lastKnownLocationEnabled.

Enabling this option, by setting FollowAnalyticsConfiguration.lastKnownLocationEnabled to true, will allow the SDK to send a location logs whenever a new session is created and a valid location is available.

The SDK is started by default with FollowAnalyticsConfiguration.lastKnownLocationEnabled set to false.

User Attributes

A user can be defined by setting its identifier. This identifier is a string that you provide to the SDK to uniquely identify a user of your app. It can be an e-mail address, an identifier from your own backend (for instance a primary key), a phone number, or anything else that could allow you to uniquely identify a user.

Attributes (like first name, date of birth...) can be associated to a user if it's defined. Attributes are associated to the device when no user is defined.

When a user is defined, a user profile is created server-side and can be shared across apps.

You can set predefined as well as custom attributes.

Define a user

To define a user, set its user ID:

FollowAnalytics.setUserId("UniqueIdentifier")

To remove a user (for example in case of a sign out), just pass nil to setUserId:

FollowAnalytics.setUserId(nil)

Predefined attributes

To set predefined attributes, you can call the following methods on the userAttributes property provided by the SDK:

- (void)setFirstName:(nullable NSString*)firstName;
- (void)setLastName:(nullable NSString*)lastName;
- (void)setEmail:(nullable NSString*)email;
- (void)setDateOfBirth:(nullable NSDate*)dateOfBirth;
- (void)setGender:(FollowAnalyticsGender)gender;
- (void)setCountry:(nullable NSString*)country;
- (void)setCity:(nullable NSString*)city;
- (void)setRegion:(nullable NSString*)region;
- (void)setProfilePictureUrl:(nullable NSString*)profilePictureUrl;
func setFirstName(firstName : String?)
func setLastName(lastName: String?)
func setEmail(email : String?)
func setDateOfBirth(dateOfBirth : Date?)
func setGender(gender : FollowAnalyticsGender)
func setCountry(country : String?)
func setCity(region : String?)
func setRegion(city : String?)
func setProfilePictureUrl(profilePictureUrl : String?)

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

[FollowAnalytics.userAttributes setFirstName:@"Joe"];
[FollowAnalytics.userAttributes setCity:@"Paris"];
FollowAnalytics.userAttributes.setFirstName("Joe")
FollowAnalytics.userAttributes.setCity("Paris")

Custom attributes

In addition to predefined attributes, you can set your own custom attributes.

Always 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. Ensure the attribute types match. This could be done by comparing with the ones you have in the Profile Data tab in the Administration page on FollowAnalytics platform.

Set a custom attribute

To set custom attributes, you can call the following methods on the userAttributes property provided by the SDK:

- (void)setInteger:(NSInteger)integerValue forKey:(nonnull NSString*)key;
- (void)setDouble:(double)doubleValue forKey:(nonnull NSString*)key;
- (void)setString:(nonnull NSString*)string forKey:(nonnull NSString*)key;
- (void)setBoolean:(bool)boolean forKey:(nonnull NSString*)key;
- (void)setDate:(nonnull NSDate*)date forKey:(nonnull NSString*)key;
- (void)setDateTime:(nonnull NSDate*)dateTime forKey:(nonnull NSString*)key;
- (void)clear:(nonnull NSString*)key;
- (void)add:(nonnull NSSet<NSString*>*)values toSet:(nonnull NSString*)key;
- (void)remove:(nonnull NSSet<NSString*>*)values toSet:(nonnull NSString*)key;
- (void)clearSet:(nonnull NSString*)key;
func setInteger(integerValue: Int, forKey: String)
func setDouble(doubleValue : Double, forKey: String)
func setString(string: String, forKey: String)
func setBoolean(boolean: Bool, forKey: String)
func setDate(date: Date, forKey: String)
func setDateTime(dateTime: Date, forKey: String)
func clear(key: String)
func add(values: Set<String>, toSet: String)
func remove(values: Set<String>, toSet: String)
func clearSet(key: String)

For example, to set the user's job:

[FollowAnalytics.userAttributes setString:@"Taxi driver" forKey:@"job"];
FollowAnalytics.userAttributes.setString("Taxi driver", forKey:"job")
Delete a custom attribute value

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

[FollowAnalytics.userAttributes clear:@"job"];
FollowAnalytics.userAttributes.clear("job")
Set of Attributes

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

To add an item to a set:

NSSet *set = [NSSet setWithObjects:@"apple", @"strawberry", @"lemon", nil];
[FollowAnalytics.userAttributes add:set toSet:@"fruits"]; // Adds "apple", "strawberry" and "lemon" to set "fruits".
FollowAnalytics.userAttributes.add(["apple", "strawberry", "lemon"], toSet:"fruits") // Adds "apple", "strawberry" and "lemon" to set "fruits".

To remove an item from a set:

NSSet *set = [NSSet setWithObjects:@"lemon", nil];
[FollowAnalytics.userAttributes remove:set toSet:@"fruits"]; // Removes "lemon" from set "fruits".
FollowAnalytics.userAttributes.remove(["lemon"], toSet:"fruits") // Removes "lemon" from set "fruits".

To clear a set:

[FollowAnalytics.userAttributes clearSet:@"fruits"]; // Removes all items from set "fruits".
FollowAnalytics.userAttributes.clearSet("fruits") // Removes all items from set "fruits".

Opt-in Analytics

FollowAnalytics SDK opt-in analytics state define whether to send logs and attributes to the platform. Opt-in analytics is true by default, meaning logs and attributes will be sent to FollowAnalytics platform.

You can set it to false at SDK initialization by setting the optInAnalyticsDefault parameter to false in the FollowAnalyticsConfiguration object passed to the initialization method. This is only used to set the default opt-in value at the first launch of the app.

This opt-in analytics value can be changed at runtime and it will be persisted for all future launch of the app.

To change opt-in analytics state at runtime, use the setOptInAnalytics method:

+ (void)setOptInAnalytics:(BOOL)state;
static func setOptIn(state: Bool)

This value is persisted in memory and the opt-in analytics state is changed for all subsequent launch of your application, meaning optInAnalyticsDefault is ignored.

To retrieve the current opt-in analytics state, use the getOptInAnalytics method:

+ (BOOL)getOptInAnalytics;
static func Bool getOptInAnalytics()

GDPR

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]
FollowAnalytics.gdpr.requestToAccessMyData()
FollowAnalytics.gdpr.requestToDeleteMyData()

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

Campaigns

From the FollowAnalytics platform, you can create:

The SDK take care of displaying campaigns, and provide you ways to customize and interact with messages received from the platform. In this section you will see how to:

Notification Service Extension Framework

Rich notifications are notifications with embedded rich media (images, GIFs, videos). When defining a Push campaign on the platform, you can add a rich media to the notification.

A badge is the number displayed on the icon of the app, indicating to the user that the app has new information. On the platform, you can set by how much the badge is to be increased or decreased at the notification reception on the user's device. This is defined as the badge increment in the Campaign Editor.

The SDK allows your app to receive rich notifications and to manage badges thanks to the FANotificationExtension notification service extension framework. In this section, we will see how to install and use this extension framework by:

Notification Service Extension and iOS version

FANotificationExtension is only available on iOS 10.0 and above. If a user has a device with iOS version under 10.0, he will receive rich notifications but not be able to see the rich media included. Also he will not able to see your app icon's badge change done through the extension framework.

Add the Notification Service Extension target

To make your app able to receive 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, let's say Notification Service, and confirm. Then when prompted, activate the scheme.



  3. Set the app as the Executable of the extension:

    • Choose the extension
    • Click Edit Scheme



    • Select app in executable field



Install FANotificationExtension from CocoaPods

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', '1.0.3', :source => 'https://github.com/followanalytics/fa-pod-spec.git'

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

Install FANotificationExtension manually

If you want to manually integrate the .framework, extract the FollowAnalytics SDK archive and copy the FANotificationExtension.framework into your project directory.

  1. Select the target of your main app, in Build Phases

    1. Press the + button, and select New Copy File Phase.
    2. Change the destination to Frameworks.
    3. Click in add other.
    4. Add the FANotificationExtension.framework file that you previously copied into your project directory.
    5. Uncheck Copy items if needed box.
  2. Still in the Build Phases

    1. Press the + button, and select New Run Script Phase.
    2. Add this script:

      bash "PATH_TO_YOUR_PROJECT_DIRECTORY/FANotificationExtension.framework/strip-frameworks.sh"
      

      This step is required to work around an App Store submission bug when archiving universal binaries.

  3. In Build Settings -> Framework Search Paths entry, add the path to where the FANotificationExtension.framework is located (actually your project directory).

Set an App Group for both app and extension targets

Update your provisioning profiles

Make sure to re-download all the provisioning profiles for application and notification service extension targets.

Configuring the SDK to work with the extension

First, make sure you have set the proper App Group for the app target:


The appGroup parameter should be specified in the configuration init of the SDK:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
    configurationWith:^(FollowAnalyticsConfiguration* _Nonnull config) {
        ...
        config.appGroup = @"group.your.identifier";
    };
}];
let configuration = FollowAnalyticsConfiguration.init { (config) in
    ...
    config.appGroup = "group.your.identifier"
}

Implement the Notification Service Extension

First, make sure you have set the proper App Group for the extension target:


Copy and paste the following implementation in the NotificationService file of the extension you created

#import “NotificationService.h”
#import <FANotificationExtension/FANotificationExtension.h>

@interface NotificationService ()

@property(nonatomic, strong) void (^contentHandler)(UNNotificationContent* contentToDeliver);
@property(nonatomic, strong) UNMutableNotificationContent* bestAttemptContent;

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest*)request
   withContentHandler:(void (^)(UNNotificationContent* _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    [FANotificationService
        getFAContentIfNeededWithRequest:request
                            bestContent:self.bestAttemptContent
                            appGroup:@"group.your.identifier"
                            completion:^(UNMutableNotificationContent* _Nullable newContent) {
                            NSLog(@"NotificationService: %@", request);
                            // Modify the notification content here...

                            self.contentHandler(newContent);
                            }];
}

- (void)serviceExtensionTimeWillExpire {
    // Called just before the extension will be terminated by the system.
    // Use this as an opportunity to deliver your "best attempt" at modified content,
    //otherwise the original push payload will be used.
    self.contentHandler(self.bestAttemptContent);
}
@end
import UserNotifications
import FANotificationExtension

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest,
                                withContentHandler contentHandler:
                                @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent,
        let contentHandler = self.contentHandler {
        // Modify the notification content here...

            FANotificationService.getFAContentIfNeeded(with: request,
                                                        bestContent: bestAttemptContent,
                                                        appGroup: "group.your.identifier") {
                                                        (newContent) in
                                                        if let newC = newContent {
                                                            contentHandler(newC)
                                                        }
            }
        }
    }

    override func serviceExtensionTimeWillExpire() {
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }
}

Badge Management

Prerequisites

To use this feature, make sure you have installed and configured FANotificationExtension properly. More information here

Badges are the numbers displayed on the icon of the app, indicating to the user that the app has new information. Our service offers you two ways to update this value. The first one through the campaign editor on our platform and the second one using our SDK methods.

Using our platform, when creating a Push Campaign, it's possible to set a badge increment so that the SDK updates the badge number at push reception.

Using our SDK you can set, increment or get the badge number through the following methods respectively:

[FABadge setBadge:INTEGER]; // Set the value of the icon badge number
[FABadge updateBadgeBy:INTEGER]; // Update the value of the icon badge number
[FABadge badge]; // Get the value of the icon badge number
FABadge.setBadge(Int) // Set 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

Prefer to use FABadge instead of UIKit

Even though you can update the badge number programmatically using the Apple UIKit applicationIconBadgeNumber, it is required to use our FABadge public methods to keep incrementing correctly the badge number on future push receptions.

Geofencing triggers and location conditions

On the platform, when creating a campaign with contextual delivery, there are two ways to use locations:

Logging locations has no effect on geofencing and location conditions

Logging locations through the logLocation method will send location events to the FollowAnalytics platform so you can use locations while targeting specific audiences. This has no effect on the Geofencing Events nor Location Condition features of contextual campaigns.

Implement custom behavior on URL opening

From the FollowAnalytics platform, you can add URLs (website links or custom URL schemes) to your Push or In-App campaigns. By default, those links will be opened by the SDK, unless the shouldOpenURL callback is defined on the FollowAnalyticsConfiguration provided at SDK initialization. This callback allows you to implement your own URL opening logic. Use the return value of this callback to allow the SDK to actually open the URL or not:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
    configurationWith:^(FollowAnalyticsConfiguration* _Nonnull config) {
        config.shouldOpenURL =
            ^BOOL(NSURL* _Nonnull url) {

            // YOUR BEHAVIOR

            // Returning `YES` tells the SDK to open the URL.
            return YES;
        }
    };
}];
let configuration = FollowAnalyticsConfiguration.init { (config) in
    config.shouldOpenURL = { (url) in

        // YOUR BEHAVIOR

        // Returning `true` tells the SDK to open the URL.
        return true
    }
}

Universal Links allow your app to be opened to handle links that point to your website. Due to the behavior of the open(_:options:completionHandler:) method, FollowAnalytics SDK will not open those links in your app, but in the browser. See this article from Apple documentation for more details.

In order to open those Universal Links in your app, you must implement the shouldOpenURL callback to forward the url to your application by calling the application(_:continue:restorationHandler:) callback:

config.shouldOpenURL = ^BOOL(NSURL* _Nonnull url) {

    BOOL urlHasBeenHandled = NO;

    // We only want to hanlde Universal Links
    // (i.e. http links that point to your own website).
    if ([url.absoluteString hasPrefix:@"http"]
        && [url.host isEqualToString:@"YOUR_WEBSITE_DOMAIN"]) {

        NSUserActivity* userActivity =
        [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb];

        userActivity.webpageURL = url;

        urlHasBeenHandled = [self application:UIApplication.sharedApplication
                            continueUserActivity:userActivity
                            restorationHandler:^(NSArray<id<UIUserActivityRestoring>> *
                                                _Nullable restorableObjects) { }];
    }

    // If the url has been handled by the application,
    // we don't want FA SDK to open the url.
    return !urlHasBeenHandled;
};
config.shouldOpenURL = { (url) in

    var urlHasBeenHandled = false

    // We only want to hanlde Universal Links
    // (i.e. http links that point to your own website).
    if url.absoluteString.hasPrefix("http")
        && url.host == "YOUR_WEBSITE_DOMAIN" {

        let userActivity = NSUserActivity(activityType:
            NSUserActivityTypeBrowsingWeb)

        userActivity.webpageURL = url

        urlHasBeenHandled = self.application(UIApplication.shared,
                                                continue: userActivity,
                                                restorationHandler: { _ in })
    }

    // If the url has been handled by the application,
    // we don't want FA SDK to open the url.
    return !urlHasBeenHandled
}

Make sure your implementation of application(_:continue:restorationHandler:) returns false when your app is not able to handle the provided url:

- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:
(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {

    if (![userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
        return NO;
    }

    if (/* Check if userActivity.webpageURL should open screen 1 */) {

        // Present screen 1 ViewController

        return YES;
    }
    else if (/* Check if userActivity.webpageURL should open screen 2 */) {

        // Present screen 2 ViewController

        return YES;
    }

    return NO;
}
func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([Any]?) -> Void) -> Bool {

    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb else { return false }

    if /* Check if userActivity.webpageURL should open screen 1 */ {

        // Present screen 1 ViewController

        return true
    }
    else /* Check if userActivity.webpageURL should open screen 2 */ {

        // Present screen 2 ViewController

        return true
    }

    return false
  }

Interactive Notifications

An Interactive Notification is a notification containing custom action buttons that the user can access by revealing the notification:

When tapped, an action button will:

To create a campaign with Interactive Notifications:

NSString* const ActionIdentifierView = @"View";
NSString* const ActionIdentifierDelete = @"Delete";
NSString* const ActionIdentifierRemind = @"Remind me tomorrow";
let ActionIdentifierView = "View"
let ActionIdentifierDelete = "Delete"
let ActionIdentifierRemind = "Remind me tomorrow"
if (@available(iOS 10.0, *)) {

    UNNotificationAction *viewAction = [UNNotificationAction
                                        actionWithIdentifier:ActionIdentifierView
                                        title:NSLocalizedString(ActionIdentifierView, nil)
                                        options:UNNotificationActionOptionForeground];

    UNNotificationAction *deleteAction = [UNNotificationAction
                                           actionWithIdentifier:ActionIdentifierDelete
                                           title:NSLocalizedString(ActionIdentifierDelete, nil)
                                           options:UNNotificationActionOptionDestructive];

    UNNotificationAction *remindAction = [UNNotificationAction
                                         actionWithIdentifier:ActionIdentifierRemind
                                         title:NSLocalizedString(ActionIdentifierRemind, nil)
                                         options:UNNotificationActionOptionForeground];

    // The UNNotificationCategoryOptionCustomDismissAction option allows to
    // call the onNotificationTapped callback when the notiifcation is dismissed
    UNNotificationCategory *newContentCategory = [UNNotificationCategory
                                                 categoryWithIdentifier:@"NewContent"
                                                 actions:@[viewAction, deleteAction, remindAction]
                                                 intentIdentifiers:@[]
                                                 options:UNNotificationCategoryOptionCustomDismissAction];

    [[UNUserNotificationCenter currentNotificationCenter]
     setNotificationCategories:[NSSet setWithObject:newContentCategory]];
}
if #available(iOS 10.0, *) {

  let viewAction = UNNotificationAction(identifier: ActionIdentifierView,
                                        title: NSLocalizedString(ActionIdentifierView,
                                                                 comment: ""),
                                        options: .foreground)

  let deleteAction = UNNotificationAction(identifier: ActionIdentifierDelete,
                                          title: NSLocalizedString(ActionIdentifierDelete,
                                                                   comment: ""),
                                          options: .destructive)

  let remindAction = UNNotificationAction(identifier: ActionIdentifierRemind,
                                          title: NSLocalizedString(ActionIdentifierRemind,
                                                                   comment: ""),
                                          options: .foreground)

  // The customDismissAction option allows to call the onNotificationTapped
  // callback when the notiifcation is dismissed
  let newContentCategory =
    UNNotificationCategory(identifier: "NewContent",
                           actions: [viewAction, deleteAction, remindAction],
                           intentIdentifiers: [],
                           options: .customDismissAction)

  UNUserNotificationCenter.current().setNotificationCategories([newContentCategory])
}

Note that the notification action title is a localized version of the actionIdentifier, while the platform displays the actionIdentifier as is in the Push Opened stats.

Implement custom behavior on notification tap

You can implement a custom behavior when the user taps on a notification by implementing the onNotificationTapped callback in FollowAnalyticsConfiguration. This allows you to perform specific actions when a notification is tapped:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
    configurationWith:^(FollowAnalyticsConfiguration* _Nonnull config) {
        config.onNotificationTapped =
            ^(FANotificationInfo* _Nonnull notificationInfo, NSString* _Nonnull actionIdentifier) {

            if ([actionIdentifier isEqualToString:FANotificationDefaultActionIdentifier]) {
                // Your behavior for a tap on the notification body

            } else if ([actionIdentifier isEqualToString:FANotificationDismissActionIdentifier]) {

                // Your behavior when the notification is dismissed (only for Interactive Notification)

            } else if ([actionIdentifier isEqualToString:ActionIdentifierView]) {

                // Your behavior for a tap on the "View" action button

            } else if ([actionIdentifier isEqualToString:ActionIdentifierDelete]) {

                // Your behavior for a tap on the "Delete" action button

            } else if ([actionIdentifier isEqualToString:ActionIdentifierRemind]) {

                // Your behavior for a tap on the "Remind me tomorrow" action button

            }
        }
    };
}];
let configuration = FollowAnalyticsConfiguration.init { (config) in
    config.onNotificationTapped = { (notificationInfo, actionIdentifier) in

        switch (actionIdentifier) {

        case FANotificationDefaultActionIdentifier:
          // Your behavior for a tap on the notification body

        case FANotificationDismissActionIdentifier:
          // Your behavior when the notification is dismissed (only for Interactive Notification)

        case ActionIdentifierView:
          // Your behavior for a tap on the "View" action button

        case ActionIdentifierDelete:
          // Your behavior for a tap on the "Delete" action button

        case ActionIdentifierRemind:
          // Your behavior for a tap on the "Remind me later" action button

        default:
          // Do nothing
        }
    }
}

Add background fetch capability

For your app to be woken-up to execute your implementation of onNotificationTapped when your app is in background, you must activate the Background Fetch capability in the Background Modes. This will be the case when the user tap a category notification button that does not open the app.

The SDK passes some Push campaign information to your app through this callback thanks to the notificationInfo argument, while the actionIdentifier argument provides information about which notification button has been tapped:

Implement custom behavior on Push reception

You can implement a custom behavior when the device receives a message from a Push campaign that contains custom parameters (i.e. at least one key/value pair). This can be used to fetch data from your server if a particular custom parameter is defined in the Push message. To do so, implement the onNotificationReceived callback in FollowAnalyticsConfiguration:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
    configurationWith:^(FollowAnalyticsConfiguration* _Nonnull config) {
        config.onNotificationReceived = ^(FANotificationInfo * _Nonnull notificationInfo){
            // YOUR BEHAVIOR
        }
    };
}];
let configuration = FollowAnalyticsConfiguration.init { (config) in
    config.onNotificationReceived = { (notificationInfo) in
        // YOUR BEHAVIOR
    }
}

Add background fetch capability

For your app to be woken-up to execute your implementation of onNotificationReceived when your app is in background, you must activate the Background Fetch capability in the Background Modes. Note that your code implemented in in this callback must not take more that 30s to execute. Otherwise the callback execution will be stopped by iOS. As a consequence, when using this callback to fetch data, make sure data size not too big compared to the data connection speed of your users.

Implement only synchronous calls

The SDK executes your onNotificationReceived code in a background thread and expects that the whole callback implementation is synchronous. Any asynchronous call is not guaranteed to finish before the end of the callback execution and then will be terminated at the time the callback execution is finished. Make sure that your implementation follows those rules, to ensure proper runtime execution:

  • All statements are synchronous calls.
  • No call to any API that should be called on the main thread.

The SDK passes some Push campaign information to your app through this callback thanks to the notificationInfo argument:

Implement custom behavior on Native Alert button tap

You can implement a custom behavior when the user taps on a Native Alert* button by implementing the onNativeInAppButtonTapped callback in the FollowAnalyticsConfiguration. This allows you to perform specific actions when a native alert button is tapped:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
    configurationWith:^(FollowAnalyticsConfiguration* _Nonnull config) {
        config.onNativeInAppButtonTapped =
            ^(FACustomButtonInfo* _Nonnull buttonInfo) {
            // YOUR BEHAVIOR
        }
    };
}];
let configuration = FollowAnalyticsConfiguration.init { (config) in
    config.onNativeInAppButtonTapped = { (buttonInfo) in
        // YOUR BEHAVIOR
    }
}

The SDK passes some Native Alert campaign information to your app through this callback thanks to the buttonInfo argument:

Controlling In-App campaigns

To pause and resume campaigns, add the following methods in you code at the location you wish the pause and resume to take effect:

[FollowAnalytics.inApp pauseCampaignDisplay]
[FollowAnalytics.inApp resumeCampaignDisplay]
FollowAnalytics.inApp.pauseCampaignDisplay()
FollowAnalytics.inApp.resumeCampaignDisplay()

Create safe spaces for you in-app messages

Rather than pause everywhere you have an important screen or process, you can pause right at the initialization of the SDK and resume in the areas you think it is safe for in-app campaigns to be displayed.

Archiving messages

FollowAnalytics SDK allows you to store campaigns's messages received by your app. This makes them available for custom usage, like building an inbox feature. All campaigns displayed by a device can be archived locally and accessed from the developer's code, formatted as a FAMessage object. In order to configure your campaign storage, you have to set the following FollowAnalyticsConfiguration properties at SDK initialization:

@property(nonatomic, readonly, assign) BOOL archivePushMessages;
@property(nonatomic, readonly, assign) BOOL archiveInAppMessages;
open var archivePushMessages: Bool { get }
open var archiveInAppMessages: Bool { get }

In FollowAnalyticsInApp and FollowAnalyticsPush protocols you will find all necessary methods to manage all FAMessage objects that are archived. They are implemented by FollowAnalytics.inApp and FollowAnalytics.push

- (nonnull NSArray<FAMessage*>*)getAll;
- (nullable FAMessage*)get:(nonnull NSString*)identifier;
- (void)markAsRead:(nonnull NSArray<NSString*>*)identifiers;
- (void)markAsUnread:(nonnull NSArray<NSString*>*)identifiers;
- (void)deleteIdentifiers:(nonnull NSArray<NSString*>*)identifiers;
func getAll() -> [FAMessage]
func get(identifier : String) -> FAMessage
func markAsRead(identifiers : [String])
func markAsUnread(identifiers : [String])
func deleteIdentifiers(identifiers : [String])

Here is a description of some of the FAMessage attributes:

Property Type Description
isRead boolean Indicates if the message has been read not not. False at message reception. Only modifiable by the InBoxManager.
isPush boolean true if message comes from a Push campaign, false if it comes from a In-App campaign.
isInApp boolean true if message comes from a In-App campaign, false` if it comes from a Push campaign.
isSilent boolean true if message comes from a Push campaign with the "Silent" option enabled (silent push), false otherwise.
identifier string If message comes from a Push campaign, this is the Apple notification identifier. If message comes from an In-App campaign, this is the FollowAnalytics campaign identifier (equals to campaignId property).
dateReceived date If message comes from a Push campaign, this is the date at which the message is received on the device. If message comes from an In-App campaign, this is the start date of the campaign.
campaignId string FollowAnalytics campaign identifier.
title string Text provided in the platform's "Title" field of the Push campaign editor.
subtitle string Text provided in the platform's "Subtitle" field of the Push campaign editor.
body string Text provided in the platform's "Content" field of the Push campaign editor.
url string Url provided in the platform's "URL" field of In-App Custom Web Page campaign editor.
deepLinkUrl string Url provided in the platform's "App Link" field of the Push campaign editor.
params dictionary Key/Value pairs provided in the Push campaign editor.

Data Wallet

If you want to use Data Wallet, the very first step is to enable it in your app at startup. This is done by setting isDataWalletEnabled property to true in the FollowAnalyticsConfiguration object passed to the SDK initialization method.

Now that the Data Wallet is enabled, the SDK will automatically download the new policies as they are published. In order to deal with the currently known policy, 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];
// gets the current policy
var policy = FollowAnalytics.dataWallet.getPolicy()

// true if the current policy is considered accepted
var policyIsRead =  FollowAnalytics.dataWallet.isRead()

// informs the SDK that the user has accepted the current policy
FollowAnalytics.dataWallet.setIsRead(true)```

There could only be one active policy at a time. When a new policy becomes available online, the SDK will call on the main thread onDataWalletPolicyChange, callback set on the FollowAnalyticsConfiguration. To set this up before starting the SDK, include the following code:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
    configurationWith:^(FollowAnalyticsConfiguration * _Nonnull config) {
      config.isDataWalletEnabled = true;
      config.onDataWalletPolicyChange = ^{
        [self displayNewPolicy];
    };
}];
let configuration = FollowAnalyticsConfiguration.init { (config) in
  config.isDataWalletEnabled = true;
  config.onDataWalletPolicyChange = {
    //YOUR_BEHAVIOR;
  }
}

At each major policy update, there needs to be consent from the client. When the user accepts a policy call [FollowAnalytics.dataWallet setIsRead:true] to mark the current policy as "read". In other words:

The SDK records the major version of the current policy for future reference. 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.

Custom default policy

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 follows:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
  configurationWith:^(FollowAnalyticsConfiguration * _Nonnull config) {
    // assuming that you have Policy.json in you main bundle
    config.dataWalletDefaultPolicyPath = [[NSBundle mainBundle] pathForResource:@"Policy"
                                                                ofType:@"json"];
}];
let configuration = FollowAnalyticsConfiguration.init { (config) in
     config.dataWalletDefaultPolicyPath = Bundle.main.path(forResource: "Policy",
                                                              ofType: "JSON")
  }

Web Views

If your app contains web views, you can use FollowAnalytics SDK from within your HTML/JS code. This is possible thanks to the FAWKWebViewJSBridge, which you can use by following those steps:

  1. Adopt the WKUIDelegate protocol in your object of choice, typically a UIViewController.

    @interface ViewController <WKUIDelegate>
    
    class ViewController: UIViewController, WKUIDelegate {
    
  2. At the initialization of the hosting view controller, keep a strong reference to a FAWKWebViewJSBridge instance:

    @property(nonatomic, nullable) FAWKWebViewJSBridge* webViewJSBridge;
    // ...
    self.webViewJSBridge = [[FAWKWebViewJSBridge alloc] init];
    
    var webViewJSBridge: FAWKWebViewJSBridge?
    // ...
    self.webViewJSBridge = FAWKWebViewJSBridge()
    
  3. Add the FAWKWebViewJSBridge instance to the WKWebViewConfiguration instance, pass this configuration to the WKWebView and set your UIViewController as the web view delegate:

    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    [self.webViewJSBridge addToConfiguration:configuration];
    WKWebView* webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
    webView.UIDelegate = self;
    
    let configuration = WKWebViewConfiguration()
    self.webViewJSBridge?.add(to: configuration)
    let webView = WKWebView.init(frame: self.view.frame, configuration: configuration)
    webView.uiDelegate = self
    
  4. Implement the following 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];
          return;
      }
    }
    
    func webView(_ webView: WKWebView,
                 runJavaScriptTextInputPanelWithPrompt prompt: String,
                 defaultText: String?,
                 initiatedByFrame frame: WKFrameInfo,
                 completionHandler: @escaping (String?) -> Void) {
      if prompt.hasPrefix("FollowAnalytics") {
        self.webView(webView,
                     runJavaScriptTextInputPanelWithPrompt: prompt,
                     defaultText: defaultText,
                     initiatedByFrame: frame,
                     completionHandler: completionHandler
        return
      }
    }
    

Tagging events/errors/locations can be performed in the HTML file by using the following methods:

FollowAnalytics.logError('err1', 'str value')
FollowAnalytics.logEvent('event1', 'str value')
FollowAnalytics.logLocation(-21.544805, 165.6629343)

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

Disabling Swizzling

By default, the SDK swizzles your UIApplicationDelegate methods and your UNUserNotificationCenterDelegate methods. This is an automatic process that allows the SDK to receive the necessary system calls seamlessly and behave accordingly without additional configurations and calls from the developer.

But in some situations, you could have the need to disable this feature. For example due to a legacy framework for some incompatibility with the swizzled methods.

In these cases we allow the developer to disable the Swizzling. To that, include the swizzlingEnabled options in the configuration as follows:

FollowAnalyticsConfiguration* configuration = [FollowAnalyticsConfiguration
  configurationWith:^(FollowAnalyticsConfiguration * _Nonnull config) {
    config.swizzlingEnabled = NO;
}];
let configuration = FollowAnalyticsConfiguration.init { (config) in
     config.swizzlingEnabled = false
  }

Required Methods

By disabling the swizzling the SDK requires the developer to forward the following methods without exception. This is mandatory to ensure the SDK works as expected. Not doing so may cause unexpected side-effects.

The required UIApplicationDelegate methods are:

  1. didRegisterForRemoteNotificationsWithDeviceToken:

    Forwarding this method the SDK will be able to send the device token to the server for Push Notifications delivery.

    - (void) application:(UIApplication*) application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*) deviceToken {
      [FollowAnalytics didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
    }
    
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
      FollowAnalytics.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
    }
    
  2. applicationDidEnterBackground:

    Forwarding this method the SDK will be able to calculate session duration, handle app badge number and detect campaign triggers.

    - (void) applicationDidEnterBackground:(UIApplication*_Nonnull) application {
      [FollowAnalytics applicationDidEnterBackground:application];
    }
    
    func applicationDidEnterBackground(_ application: UIApplication) {
      FollowAnalytics.applicationDidEnterBackground(application)
    }
    
  3. applicationWillEnterForeground:

    Forwarding this method the SDK will be able to synchronize campaigns, detect campaign triggers and present queued triggered campaigns.

    - (void) applicationWillEnterForeground:(UIApplication*_Nonnull) application {
      [FollowAnalytics applicationWillEnterForeground:application];
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
      FollowAnalytics.applicationWillEnterForeground(application)
    }
    
  4. applicationDidBecomeActive:

    Forwarding this method the SDK will be able to calculate sessions durations and detect campaign triggers.

    - (void) applicationDidBecomeActive:(UIApplication*_Nonnull) application {
      [FollowAnalytics applicationDidBecomeActive:application];
    }
    
    func applicationDidBecomeActive(_ application: UIApplication) {
      FollowAnalytics.applicationDidBecomeActive(application)
    }
    
  5. applicationWillTerminate:

    Forwarding this method the SDK will be able to calculate sessions durations and handle app badge number.

    - (void) applicationWillTerminate:(UIApplication*_Nonnull) application {
      [FollowAnalytics applicationWillTerminate:application];
    }
    
    func applicationWillTerminate(_ application: UIApplication) {
      FollowAnalytics.applicationWillTerminate(application)
    }
    
  6. applicationDidReceiveMemoryWarning:

    Forwarding this method the SDK will be able to send statistics information.

    - (void) applicationDidReceiveMemoryWarning:(UIApplication*_Nonnull) application {
      [FollowAnalytics applicationDidReceiveMemoryWarning:application];
    }
    
    func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
      FollowAnalytics.applicationDidReceiveMemoryWarning(application)
    }
    
  7. didReceiveRemoteNotification:

    Forwarding this method the SDK will be able to handle Push Notifications and In-App campaigns.

    - (void) application:(UIApplication*_Nonnull) application didReceiveRemoteNotification:(NSDictionary*_Nullable) userInfo {
      [FollowAnalytics application:application didReceiveRemoteNotification:userInfo];
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
      FollowAnalytics.application(application, didReceiveRemoteNotification: userInfo)
    }
    
  8. handleActionWithIdentifierForRemoteNotification:

    Forwarding this method the SDK will be able to handle Push Notifications and In-App campaigns.

    - (void) application:(UIApplication*_Nonnull) application handleActionWithIdentifier:(NSString*_Nullable) identifier forRemoteNotification:(NSDictionary*_Nonnull) userInfo completionHandler:(void (^_Nullable)(void)) completionHandler {
      [FollowAnalytics application:application handleActionWithIdentifier:identifier forRemoteNotification:userInfo completionHandler:completionHandler];
    }
    
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable: Any], completionHandler: @escaping () -> Void) {
      FollowAnalytics.application(application, handleActionWithIdentifier: identifier, forRemoteNotification: userInfo, completionHandler: completionHandler)
    }
    
  9. handleActionWithIdentifierForLocalNotification:

    Forwarding this method the SDK will be able to handle Notifications and In-App campaigns.

    - (void) application:(UIApplication*_Nonnull) application handleActionWithIdentifier:(NSString*_Nullable) identifier forLocalNotification:(UILocalNotification*_Nonnull) notification completionHandler:(void (^_Nullable)(void)) completionHandler {
      [FollowAnalytics application:application handleActionWithIdentifier:identifier forLocalNotification:notification completionHandler:completionHandler];
    }
    
    func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
      FollowAnalytics.application(application, handleActionWithIdentifier: identifier, for: notification, completionHandler: completionHandler)
    }
    
  10. handleOpenURL:

    If your application support iOS version less than 9.0.

    Forwarding this method the SDK will be able to present alerts about the current user status if the URL is related to this SDK.

    - (BOOL) application:(UIApplication*_Nonnull) application handleOpenURL:(NSURL*_Nonnull) url {
      return [FollowAnalytics application:application handleOpenURL:url];
    }
    
    func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
      return FollowAnalytics.application(application, open: url, options: options)
    }
    
  11. openURLSourceApplication:

    If your application support iOS version less than 9.0.

    Forwarding this method the SDK will be able to present alerts about the current user status if the URL is related to this SDK.

    - (BOOL) application:(UIApplication*_Nonnull) application openURL:(NSURL*_Nonnull) url sourceApplication:(NSString*_Nullable) sourceApplication annotation:(id _Nonnull) annotation {
      return [FollowAnalytics application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
    }
    
    func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
      return FollowAnalytics.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation)
    }
    
  12. openURLOptions:

    Forwarding this method the SDK will be able to present alerts about the current user status if the URL is related to this SDK.

    - (BOOL) application:(UIApplication*_Nonnull) application handleOpenURL:(NSURL*_Nonnull) url {
      return [FollowAnalytics application:application handleOpenURL:url];
    }
    
    func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
      return FollowAnalytics.application(application, open: url, options: options)
    }
    
  13. didReceiveLocalNotification:

    Forwarding this method the SDK will be able to handle In-App campaigns.

    - (void) application:(UIApplication*_Nonnull) application didReceiveLocalNotification:(UILocalNotification*_Nonnull) notification {
      [FollowAnalytics application:application didReceiveLocalNotification:notification];
    }
    
    func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
      FollowAnalytics.application(application, didReceive: notification)
    }
    
  14. performFetchWithCompletionHandler:

    Forwarding this method the SDK will be able to close sessions and fetch contents.

    - (void) application:(UIApplication*_Nonnull) application performFetchWithCompletionHandler:(void (^_Nullable)(UIBackgroundFetchResult)) completionHandler {
      [FollowAnalytics application:application performFetchWithCompletionHandler:completionHandler];
    }
    
    func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
      FollowAnalytics.application(application, performFetchWithCompletionHandler: completionHandler)
    }
    
  15. didReceiveRemoteNotificationFetchCompletionHandler:

    Forwarding this method the SDK will be able to synchronize campaigns, handle the badge icon and detect campaign triggers.

    - (void) application:(UIApplication*_Nonnull) application didReceiveRemoteNotification:(NSDictionary*_Nonnull) userInfo fetchCompletionHandler:(void (^_Nonnull)(UIBackgroundFetchResult)) completionHandler {
      [FollowAnalytics application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
      FollowAnalytics.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
    }
    
  16. handleEventsForBackgroundURLSession:

    Forwarding this method the SDK will be able to synchronize campaigns, handle the badge icon and detect campaign triggers.

    - (void) application:(UIApplication*_Nonnull) application handleEventsForBackgroundURLSession:(NSString*_Nonnull) identifier completionHandler:(void (^_Nullable)(void)) completionHandler {
      [FollowAnalytics application:application handleEventsForBackgroundURLSession:identifier completionHandler:completionHandler];
    }
    
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
      FollowAnalytics.application(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
    }
    

The required UNUserNotificationCenterDelegate methods are:

  1. didReceiveNotificationResponse:

    Forwarding this method the SDK will be able to handle Push Notifications and In-App campaigns.

    - (void) userNotificationCenter:(UNUserNotificationCenter*) center didReceiveNotificationResponse:(UNNotificationResponse*) response withCompletionHandler:(void (^)(void)) completionHandler {
      [FollowAnalytics userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
      FollowAnalytics.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
    }
    
  2. willPresentNotification:

    Forwarding this method the SDK will be able to handle Push Notifications and In-App campaigns.

    - (void) userNotificationCenter:(UNUserNotificationCenter*) center willPresentNotification:(UNNotification*) notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions)) completionHandler {
      [FollowAnalytics userNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler];
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
      FollowAnalytics.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
    }
    

Configuration

To customize the SDK behavior, you must define your desired SDK parameters by setting the related properties values on the FollowAnalyticsConfiguration object passed to the SDK initialization method:

Parameter Type Default Value Description
apiKey string - Your app api key. You can find it on the FollowAnalytics platform in the Administration section.
appGroup string - An app group identifier to link the extension target to the app.
isVerbose boolean false true to see internal logs made by the SDK in the console. Note that setting isVerbose to true will automatically set apiMode to .dev. So make sure to isVerbose to false for production build. More information in the Analytics section.
apiMode enum .prod .dev or .prod for Swift, FollowAnalyticsAPIModeDev or FollowAnalyticsAPIModeProd for Objective-C. In Dev apiMode, logs, attributes and crashes sent by the SDK will be ignored by the platform, but you still can see them in the Device Observer. More information in the Analytics section.
optInAnalyticsDefault boolean true true to make the user opt-in analytics by default. More information in the Opt-in Analytics section.
crashReportingEnabled boolean true true to enable FollowAnalytics crash reporting.
isDataWalletEnabled boolean false true to enable DataWallet.
dataWalletDefaultPolicyPath string - To define the path of your dataWallet policy .
onDataWalletPolicyChange callback - Called when a new significant version of dataWallet policy is available.
archivePushMessages boolean false true to archive Push campaigns messages. More information in the Archiving messages section.
archiveInAppMessages boolean false true to archive In-App campaigns messages. More information in the Archiving messages section.
maxBackgroundTimeWithinSession int 120 To determine the lifetime of a session when in background (between 15 and 3600).
onNotificationTapped callback - Called when user taps on a notification.
onNotificationReceived callback - Called when device receives a message from a Push campaign that contains custom parameters (i.e. at least one key/value pair).
onNativeInAppButtonTapped callback - Called when user taps on a custom button of a Native Alert In-App campaign.
shouldOpenURL callback true Called when user taps on a URL (website links or custom URL schemes) from a Push or In-App campaigns. This callback returns a boolean value indicating to the SDK if it should call open(_:options:completionHandler:) or not. More information in the Implement custom behavior on URL opening section.
swizzlingEnabled boolean true false to disable the Swizzling process. More information in the Disabling swizzling section.
lastKnownLocationEnabled boolean false true to enable the logging of the last known location when the session starts.

Updating from older versions

Updating from 6.4 to 6.5

Updating from 6.4.0 to 6.4.1

Updating from 6.3 to 6.4

Updating from 6.2 to 6.3

If your app implements onIncomingDeepLink and onOpenURL callbacks:

Updating from 6.1 to 6.2

Updating to 6.0

Updating from 5.3 to 5.4

Updating from 5.2 to 5.3

Updating from 5.0 to 5.1

Updating from 4 to 5.0.0

To use version 5.0.0+ of the iOS SDK:

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

Updating from 2.* to 3.0.0

To use version 3.0.0+ of the iOS SDK:

These new parameters are provided only when running on iOS 8 and later. They are the interactive notification parameters that you would want to get to know which action was performed on a FollowAnalytics notification.

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