🚧

Deprecation Notice

This is a deprecation notice for our mobile SDKs email identification functionality. Identifying users using their user email will no longer be supported as a valid identifier for the iOS and Android SDKs.

Users currently identified by their email will not be affected. However, we recommend identifying all users with a unique user ID. Please note that users will still be associated with their user emails, which can be used for filtering and audience segmentation.

Taplytics is a native mobile A/B testing, feature management, and push notification platform that helps you optimize your iOS, tvOS, and macOS Catalyst apps, with support for:

  • iOS
  • iPad OS v10.0+
  • tvOS v10.0+
  • macOS (using Catalyst) v10.15+

The Taplytics iOS SDK is available using XCFramework with Swift Package Manager, CocoaPods or Carthage.

📘

Visual Editing Support

Mobile visual editing is supported on native iOS apps where the UI is not built using SwiftUI. SwiftUI uses Apple private classes to draw elements, this means that to stay within Apple's approved guidelines visual editing is not currently supported on SwiftUI.


Installation

First, you'll want to install our SDK inside your XCode project.


Swift Package Manager

To add the Taplytics package into XCode:

  1. Click on File and select Add Packages...
  2. Enter this git URL: https://github.com/taplytics/taplytics-ios-sdk.
  3. Add the -ObjC Linker flag to your project's Build Settings under Other Linker Flags settings.

That's it! You can then call Taplytics in your didFinishLaunchingWithOptions method in your AppDelegate. Proceed to Initialize Taplytics here.


CocoaPods

  1. Create a Podfile using pod init and add Taplytics to your podfile:
# For iOS:
  pod 'Taplytics'
# For tvOS:
  pod 'Taplytics_tvOS'
  1. Run pod install
  2. Open your project's .xcworkspace file

That's it! You can then call Taplytics in your didFinishLaunchingWithOptions method in your AppDelegate. Proceed to Initialize Taplytics here.


Carthage

As of 4.0.0, Taplytics can now be used with Carthage!

  1. Create a Cartfile and add the following:
github "Taplytics/taplytics-ios-sdk" ~> 4.0
  1. Run carthage update --use-xcframeworks, and drag the built .xcframework bundle from Carthage/Build into the "Frameworks and Libraries" section of your application’s Xcode project.

  2. Add the -ObjC Linker flag to your project's Build Settings under Other Linker Flags settings.

That's it! You can then call Taplytics in your didFinishLaunchingWithOptions method in your AppDelegate. Proceed to Initialize Taplytics here.


Manual Installation

  1. Download the SDK / clone into your app.
  2. Load the Taplytics XCFramework into your app.
  3. Add the -ObjC Linker flag to your project's Build Settings under Other Linker Flags settings.
  4. Add the required frameworks:

For iOS

CoreTelephony.framework
SystemConfiguration.framework
JavaScriptCore.framework

For tvOS

SystemConfiguration.framework
JavaScriptCore.framework

That's it! You can then call Taplytics in your didFinishLaunchingWithOptions method in your AppDelegate. Proceed to Initialize Taplytics here.


Install with Segment

The Taplytics SDK can also be installed via Segment. You can find install instructions here.


Install with mParticle

Check the Taplytics integration for the mParticle Apple SDK docs here.


Initialization

Initialize the SDK by adding the correct import for the framework you are using and the following line of code with your SDK key in your UIApplicationDelegate.

Required: startTaplyticsAPIKey must be called from the didFinishLaunchingWithOptions method as well as before you make self.window the key window through [self.window makeKeyAndVisible] or [self.window makeKeyWindow].

// For iOS
import Taplytics
// For tvOS
import Taplytics_tvOS
...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    ...
    Taplytics.startAPIKey("SDK_KEY")
    ...
    // make sure you call startTaplytics before any makeKeyWindow calls:
    // self.window.makeKeyAndVisible()
    // self.window.makeKeyWindow()
    ...
    return true
}
// iOS
 #import <Taplytics/Taplytics.h>
 // tvOS
 #import <Taplytics_tvOS/Taplytics.h>
 ...
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     ...
     [Taplytics startTaplyticsAPIKey:@"SDK_KEY"];
     ...
     // make sure you call startTaplytics before any makeKeyWindow calls:
     // [self.window makeKeyAndVisible];
     // [self.window makeKeyWindow];
     ...
     return YES;
 }

Taplytics can also be initialized with a couple additional parameters. Users can define start options to change how the SDK functions and what data it retains, more info on that here. There is also an option for users to define a callback for when Taplytics finishes loading.

Taplytics.startAPIKey("SDK_KEY", options: [
  TaplyticsOptionDisable: [TaplyticsDisableTrackLocation]
]) { (loaded) in
    // Finished loading Taplytics
}
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:[NSDictionary dictionary] callback:^(BOOL loaded) {
    // Finished loading
}]

Important Notes

It is highly recommended to initialize Taplytics in the AppDelegate’s didFinishedLaunchingWithOptions for several reasons.

  1. Taplytics uses the information on how your app was launched to support some of our core features. ie. View construction/data for the Visual Editor
  2. Taplytics should also be started as early as possible to allow time for Taplytics to fetch our config and present the correct experiment and feature data before the first activity starts.
  3. Taplytics uses the application start as a signal for when the App has been activated. We use this data to accurately track sessions and activity.

Caching & SDK Timeouts

Built into each Taplytics client-side SDK is a caching system that saves experiment and feature flag values locally on each session. These values are saved locally for several reasons, but the two primary ones are:

  1. To keep the user experience consistent when Taplytics’ servers are unreachable for any reason
  2. To minimize outbound requests to Taplytics, allowing configuration data to be used throughout a session if successfully loaded and saved

The Taplytics SDK also enables you to configure the request timeout, which specifies the time within which the Taplytics SDK must return a response. By default, the timeout value is set to 4000ms on all client-side SDKs. If the default timeout value is not appropriate for your application, you can adjust the timeout for your specific requirements within the starting parameters.

If a response isn’t returned within the time specified, the request ends. The Taplytics SDK will either return the last cached value saved within your device, or if there was no previously cached value, the SDK will use variable default values.

Whichever is used, cached values or defaults, the SDK will use those values for the remainder of the session. Updated values from our SDK will still attempt to download on timeouts in the background and will be cached and ready to use on the next session if any retry is successful.


Advanced Pairing

Note: Advanced pairing is only supported by iOS. tvOS does not support advanced pairing!

You can implement Advanced Pairing, which will allow you to pair your device to Taplytics via a link sent by email or text. Advanced Pairing is an easy way for your team to pair any build of your app to Taplytics.

  • First ensure that application:openURL:options: method is implemented in your UIApplicationDelegate
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    return false
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    return NO;
}
1548
  • Add Taplytics URL Type in XCode's Build Info panel, with Identifier: com.taplytics, add your Taplytics URL Scheme from above.
1036

Start Options

Start options allow you to control how certain SDK features function, and enable or disable features.

Start OptionTypeDescription
TaplyticsOptionDelayLoadNSNumberControls the maximum time the SDK will show your launch image for. More details.
TaplyticsOptionShowLaunchImageNSNumber(Boolean)Controls whether or not the SDK will hold the launch image during DelayLoad. Note: Disabling this feature may result in users seeing visual changes, or code variables using default values for first session
TaplyticsOptionLaunchImageTypeNSStringIf you are using a xib as a launch image set the value as @"xib". This will stop the caught exception that occurs for xib based launch images.
TaplyticsOptionShowShakeMenuNSNumber(Boolean)To disable the shake menu from showing up in development mode, set to @NO
TaplyticsOptionTestExperimentsNSDictionaryTo test specific experiments, pass in the experiment name/variation name as the key/values of a NSDictionary. More details.
TaplyticsOptionDisableBordersNSNumber(Boolean)To disable all Taplytics borders in development mode, set to @YES
TaplyticsOptionAsyncLoadingNSNumber(Boolean)Forces loading of taplytics properties from disk as async task, breaks some synchronous variable behaviour, see section below for details.
TaplyticsOptionLoggingNSNumber(Boolean)Provides more verbose logging from Taplytics to help with debugging.
TaplyticsOptionUserBucketingNSNumber(Boolean)Enables User-Based bucketing in the SDK and requires a user_id to provide consistent bucketing rather than bucketing by a device ID. Default is false. For more information please view this doc first.
TaplyticsOptionCustomFirebaseFormatNSNumber(Boolean)Allows you to pass Firebase event names in the parameters as a workaround Firebase's 500 event limit. More details
TaplyticsDisablePushNotificationSWNSNumber(Boolean)Disables swizzling of push notification delegate methods. To use push notifications properly, be sure to implement the additional methods in the push notification section.

Example:

Taplytics.startAPIKey("SDK_KEY", options: [
    TaplyticsOptionDelayLoad: 6,
    TaplyticsOptionLaunchImageType: "xib",
    TaplyticsOptionShowShakeMenu: false,
    TaplyticsOptionDisableBorders: true,
    TaplyticsOptionTestExperiments: [
        "Experiment 1": "Variation 1",
        "Experiment 2": "baseline"
    ]
])
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{
    TaplyticsOptionDelayLoad: @6,
    TaplyticsOptionLaunchImageType: @"xib",
    TaplyticsOptionShowShakeMenu: @NO,
    TaplyticsOptionDisableBorders: @YES,
    TaplyticsOptionTestExperiments: @{
          @"Experiment 1": @"Variation 1",
           @"Experiment 2": @"baseline"
    }
}];

Async Loading

Enabling the start option TaplyticsOptionAsyncLoading will make the initial loading of Taplytics properties from disk run on an async thread. However, this will break the behaviour of synchronous variables where they used the value loaded from disk, with TaplyticsOptionAsyncLoading enabled and synchronous variables are initialized before properties are loaded from disk they will use the default value. To ensure properties are loaded when initializing synchronous variables use the callback provided with startTaplyticsAPIKey or [Taplytics newSessionCallback] or [Taplytics propertiesLoadedCallback:].

Notes:

  • [Taplytics newSessionCallback] and [Taplytics propertiesLoadedCallback:] must be placed after startTaplytics.
  • Please use [Taplytics newSessionCallback] on versions 3.0.0 and above, [Taplytics propertiesLoadedCallback:] has been deprecated as of version 3.0.0.

Existing behaviour example:

Taplytics.startAPIKey("SDK_KEY")

// Existing behaviour would have loaded value from disk and the variable's value would be loaded from disk.
self.tlVar = TaplyticsVar.sync(name: "syncVar", defaultValue: 1 as NSNumber)
print("Variable Value: \(tlVar.value)")
[Taplytics startTaplyticsAPIKey:@"SDK_KEY"];

// Existing behaviour would have loaded value from disk and the variable's value would be loaded from disk.
self.var = [TaplyticsVar taplyticsSyncVarWithName:@"syncVar" defaultValue:@1];
NSLog(@"Variable Value: %@", _var.value);

Async Loading example:

Taplytics.startAPIKey("SDK_KEY", options: [TaplyticsOptionAsyncLoading: true])

// Here var.value would not be loaded from disk and would have the default value of @1
self.tlVar = TaplyticsVar.sync(name: "syncVar", defaultValue: 1 as NSNumber)
print("Variable Value: \(tlVar.value)")

// NEW on version 3.0.0 and above
// Using the newSessionCallback:
Taplytics.newSessionCallback { (loaded) in
    guard loaded, let label = self.label else {
        return
    }

    self.tlVar = TaplyticsVar.sync(name: "syncVar", defaultValue: 1 as NSNumber)
    if let tlVariable = self.tlVar, let stringValue = tlVariable.value as? String {
        print("Variable Value: \(stringValue)")
    }
}

// Deprecated on version 3.0.0
// Using the propertiesLoadedCallback:
Taplytics.propertiesLoadedCallback { (loaded) in
    guard loaded, let label = self.label else {
        return
    }

    self.tlVar = TaplyticsVar.sync(name: "syncVar", defaultValue: 1 as NSNumber)
    if let tlVariable = self.tlVar, let stringValue = tlVariable.value as? String {
        print("Variable Value: \(stringValue)")
    }
}
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{TaplyticsOptionAsyncLoading: @YES}];

// Here var.value would not be loaded from disk and would have the default value of @1
self.var = [TaplyticsVar taplyticsSyncVarWithName:@"syncVar" defaultValue:@1];
NSLog(@"Variable Value: %@", _var.value);

// NEW on version 3.0.0 and above
// Using the newSessionCallback:
__weak AppDelegate* weakSelf = self;
[Taplytics newSessionCallback:^(BOOL loaded) {
    // Now the variable will have the correct value loaded from disk/server
    weakSelf.var = [TaplyticsVar taplyticsSyncVarWithName:@"syncVar" defaultValue:@1];
    NSLog(@"Variable Value: %@", weakSelf.var.value);
}];

// Deprecated on version 3.0.0
// Using the propertiesLoadedCallback:
__weak AppDelegate* weakSelf = self;
[Taplytics propertiesLoadedCallback:^(BOOL loaded) {
    // Now the variable will have the correct value loaded from disk/server
    weakSelf.var = [TaplyticsVar taplyticsSyncVarWithName:@"syncVar" defaultValue:@1];
    NSLog(@"Variable Value: %@", weakSelf.var.value);
}];

Setting User Attributes

It's possible to segment your users based on custom user attributes. To set this up, you need to make either the user_id or email unique to identify the user across multiple devices. Below is a list of standard custom fields you can send to Taplytics. There is also a customData field that allows you to send any custom data you have as a flat NSDictionary with NSJSONSerialization accepted values.

The possible fields are:

ParameterType
emailNSString
user_idNSString
firstNameNSString
lastNameNSString
nameNSString
ageNSNumber
genderNSString
customDataNSDictionary

For example:

Taplytics.setUserAttributes([
    "user_id": "testUser",
    "name": "Test User",
    "email": "[email protected]",
    "gender": "female",
    "age": 25,
    "avatarUrl": "https://pbs.twimg.com/profile_images/497895869270618112/1zbNvWlD.png",
    "customData": [
        "paidSubscriber": true,
        "purchases": 3,
        "totalRevenue": 42.42
    ]
])
[Taplytics setUserAttributes: @{
    @"user_id": @"testUser",
    @"name": @"Test User",
    @"email": @"[email protected]",
    @"gender": @"female",
    @"age": @25,
    @"avatarUrl": @"https://pbs.twimg.com/profile_images/497895869270618112/1zbNvWlD.png",
    @"customData": @{
        @"paidSubscriber": @YES,
        @"purchases": @3,
        @"totalRevenue": @42.42
    }
}];

NOTE: The customData field is limited to 50kb of JSON string data.


User Attributes on First Launch

User Attributes set before startTaplyticsAPIKey: is called, will be used for experiment segmentation on the first session of your app. Any attributes that are set after startTaplyticsAPIKey: is called will not be used for experiment segmentation until the next session of your app.

Taplytics.setUserAttributes(["customData": ["paid_user": true]])

Taplytics.startAPIKey("SDK_KEY")

Taplytics.setUserAttributes(["customData": ["demo_account": false]])
// These custom data values will be used for segmentation on the first session of the app.
[Taplytics setUserAttributes:@{@"customData": @{@"paid_user": @YES}}];

[Taplytics startTaplyticsAPIKey:@"SDK_KEY"];

// These custom data values will only take effect on the second session of the app.
[Taplytics setUserAttributes:@{@"customData": @{@"demo_account": @NO}}];

You can also use setUserAttributes with a block that can notify you when the call to the server has completed.

Taplytics.setUserAttributes(["customData": ["paid_user": true]]) {
    // Finished setting user attributes!        
}
[Taplytics setUserAttributes:@{@"customData": @{@"demo_account": @NO}} withCallback:^(void) {
    // Finished setting user attributes!
}];

Retrieving Session Info

Taplytics also offers a method to retrieve select information of what you know about a session at a given time. This method returns the user's Taplytics identifier (appUser_id) and current session id (session_id)

Taplytics.getSessionInfo { (sessionInfo) in
    // Use the NSDictionary of sessionInfo
}
[Taplytics getSessionInfo:^(NSDictionary * _Nullable sessionInfo) {
    // Use the NSDictionary of sessionInfo
}];

Resetting Users

Once a user logs out of your app, their User Attributes are no longer valid. You can reset their data by calling resetUser:, make sure you do not set any new user attributes until you receive the callback.

Taplytics.resetUser {
  // Finished User Reset
}
[Taplytics resetUser:^{
  // Finished User Reset
}];

User Opt-In / Opt-Out

Using the User Opt-In / Opt-Out APIs allows you to simplify the process to get user consent for analytics tracking and experimentation. Calling optOutUserTracking will disable all Taplytics analytics tracking and experiments, and calling optInUserTracking will re-enable all Taplytics analytics tracking and experiments. You can retrieve the current status using: hasUserOptedOutTracking.

Taplytics.optOutUserTracking()
        
Taplytics.optInUserTracking()

let hasOptedOut = Taplytics.hasUserOptedOutTracking()
[Taplytics optOutUserTracking];

[Taplytics optInUserTracking];

BOOL hasOptedOut = [Taplytics hasUserOptedOutTracking];

Tracking Events


Automatic Tracking

The following events and general information are automatically tracked by Taplytics and will appear on your dashboard.

  • App Active/Background/Terminated
  • View Appeared/Disappeared
  • Time on View
  • Location information and Authorized/Denied events
  • Social Sign-in Authorized/Denied
  • Social Share Authorized/Denied
  • Photo Library Authorized/Denied
  • Push Notification Authorized/Denied (iOS only)
  • Push Notification Seen/Opened (iOS only)

External Analytics Sources

If you already have Analytics events instrumented with another Analytics source, Taplytics will also automatically pull in the events from the following sources:

To learn more about our Integrations, visit the 3rd Party Integrations page for more details.


Disabling Automatic Tracking

You can disable automatic tracking for any of the below constants by adding them to the disable array.

ConstantDescription
TaplyticsDisableTrackLocationlocation tracking via GPS
TaplyticsDisableTrackSocialSignInsocial sign in access alert tracking
TaplyticsDisableTrackiAdInstallationapp install from iAd
TaplyticsDisableTrackPhotoLibraryAccessphoto library access alert tracking
TaplyticsDisableTrackDeviceNamedevice name tracking (v3.4.0+)
TaplyticsDisableSourceGoogleAnalyticsGoogle Analytics event tracking
TaplyticsDisableSourceFlurryFlurry Analytics event tracking
TaplyticsDisableSourceMixpanelMixpanel Analytics event tracking
TaplyticsDisableSourceIntercomIntercom event tracking
TaplyticsDisableSourceParseParse Analytics event tracking
TaplyticsDisableSourceApsalarApsalar Analytics event tracking
TaplyticsDisableSourceAdobeAdobe Analytics event tracking
TaplyticsDisableSourceLocalyticsLocalytics Analytics event tracking
TaplyticsDisableSourceCrashlyticsCrashlytics Answers Analytics event tracking
TaplyticsDisableSourceKISSMetricsKISSMetrics Analytics event tracking
TaplyticsDisableSourceHeapHeap Analytics event tracking
TaplyticsDisableSourceFirebaseFirebase Analytics event tracking
TaplyticsDisableUITableViewSWUITableView tracking
TaplyticsDisableUICollectionViewSWUICollectionView tracking
TaplyticsDisableUIPageViewSWUIPageView tracking
TaplyticsDisableUIActionSheetSWUIActionSheet tracking
TaplyticsDisableUIAlertViewSWUIAlertView tracking
TaplyticsDisableUITextFieldViewSWUITextField tracking

For example:

Taplytics.startAPIKey("SDK_KEY", options: [
    TaplyticsOptionDisable: [TaplyticsDisableTrackLocation]
])
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{
    TaplyticsOptionDisable: @[TaplyticsDisableTrackLocation]
}];

Taplytics Events

You can also track your own custom Analytics events to Taplytics using the logEvent: methods:

Taplytics.logEvent("CustomEvent")

// With Number value and metaData
Taplytics.logEvent("CustomEvent", value: 42, metaData: ["userSubscribed": true])

// Log Revenue
Taplytics.logRevenue("Purchase", revenue: 10.15, metaData: ["Item": "blueSweater"])
[Taplytics logEvent:@"CustomEvent"];

// With NSNumber value and metaData
[Taplytics logEvent:@"CustomEvent" value:@42 metaData:@{@"userSubscribed":@YES}];

// Log Revenue
[Taplytics logRevenue:@"Purchase" value:@10.25 metaData:@{@"Item":@"blueSweater"}];

NOTE: the event metaData is limited to 50kb of JSON string data.


Firebase Events

Firebase has a limit of 500 distinct event names per project. As a way around this, you can pass in the start option TaplyticsOptionCustomFirebaseFormat and send the event names in the parameters instead with any additional metadata.

Note: If passing the event name in the parameters, you must track the event name using the key eventName.

// Normal way of logging events
Analytics.logEvent("CustomEvent", parameters: ["userSubscribed": true])
Analytics.logEvent("AnotherCustomEvent", parameters: ["userSubscribed": true])

// With start option enabled
Analytics.logEvent("tlEvents", parameters: ["eventName": "CustomEvent", "userSubscribed": true])
Analytics.logEvent("tlEvents", parameters: ["eventName": "AnotherCustomEvent", "userSubscribed": true])
// Normal way of logging events
[FIRAnalytics logEventWithName:@"CustomEvent" parameters:@{@"userSubscribed":@YES}];
[FIRAnalytics logEventWithName:@"AnotherCustomEvent" parameters:@{@"userSubscribed":@YES}];

// With start option enabled
[FIRAnalytics logEventWithName:@"tlEvents" parameters:@{@"eventName":@"CustomEvent", @"userSubscribed":@YES}];
[FIRAnalytics logEventWithName:@"tlEvents" parameters:@{@"eventName":@"AnotherCustomEvent", @"userSubscribed":@YES}];

NOTE: event metaData is limited to 50kb of JSON string data.


Experiment Information Postbacks

The Taplytics SDK leverages events from other analytics sources but we can also send a list of Experiment and Variation data back to your Analytics source to run analysis on. Check out our 3rd Party Integrations docs for more info.


Experiments

Creating experiments is easy using Taplytics. You can either use our visual editor or create code-based experiments. You can find documentation on how to do this below.

Visual Editing

You don't have to do anything else! Once you've installed the SDK and initialized Taplytics, you can use the Taplytics dashboard to make all your visual changes. See the docs on visual editing here.

Note: Only available using our iOS framework, not available on tvOS.

Dynamic Variables & Code Blocks

To see and modify these variables or blocks on the dashboard, the app must be launched and this code containing the variable or block must be navigated to a least once.

The code below is used to send the information of the variable or block to Taplytics, so it will appear on the dashboard. Taplytics variables are values in your app that are controlled by experiments. Changing the values can update the content or functionality of your app. Variables are reusable between experiments and operate in one of two modes: synchronous or asynchronous.


Synchronous

Synchronous variables are guaranteed to have the same value for the entire session and will have that value immediately after construction.

Due to the synchronous nature of the variable, if it is used before the experiments have been loaded from Taplytics servers (for example on the first launch of your app), it's value will be the default value rather than the value set for that experiment. This could taint the results of the experiment. In order to prevent this you can ensure that the experiments are loaded before using the variable. This can be done using the propertiesLoadedCallback: method, as an example:

// New as of version 3.0.0
Taplytics.newSessionCallback { (loaded) in
    loadTLVariables()
}

// Deprecated as of version 3.0.0
Taplytics.propertiesLoadedCallback { (loaded) in
    loadTLVariables()
}
// New as of version 3.0.0
[Taplytics newSessionCallback:^(BOOL loaded) {
    [self loadTLVariables];
}];

// Deprecated as of version 3.0.0
[Taplytics propertiesLoadedCallback:^(BOOL loaded) {
    [self loadTLVariables];
}];

Note that this must be done only after startTaplytics.

Synchronous variables take two parameters in its constructor:

  1. Variable name (String)
  2. Default Value

The type of the variable is defined by the type of the Default Value and can be a JSON serializable NSDictionary, NSString, NSNumber or a Boolean casted to a NSNumber.

We do support arrays within JSON code variables, however, it has to be a top level array where it would resemble something like this {"a" : [1,2,3]}.

Here's an example, using a variable of type String, using its value to get the value of the variable:

let stringVar = TaplyticsVar.sync(name: "stringVar", defaultValue: "string" as NSString)
let string = stringVar.value as? String
TaplyticsVar* stringVar = [TaplyticsVar taplyticsSyncVarWithName:@"stringVar" defaultValue:@"string"];
NSString* string = (NSString*)stringVar.value;

Using a casted Boolean to a NSNumber:

let boolVar = TaplyticsVar.sync(name: "boolVar", defaultValue: true as NSNumber)
let boolean = boolVar.value as? Bool
TaplyticsVar* boolVar = [TaplyticsVar taplyticsSyncVarWithName:@"boolVar" defaultValue:@(YES)];
BOOL boolean = [(NSNumber*)boolVar.value boolValue];

Asynchronous

Asynchronous variables take care of insuring that the experiments have been loaded before returning a value. This removes any danger of tainting the results of your experiment with bad data. What comes with the insurance of using the correct value is the possibility that the value will not be set immediately. If the variable is constructed before the experiments are loaded, you won't have the correct value until the experiments have finished loading. If the experiments fail to load, then you will be given the default value, as specified in the variables constructor.

Asynchronous variables take three parameters in its constructor:

  1. Variable name (String)
  2. Default Value
  3. TLVarBlock

Just as for synchronous variables the type of the variable is defined by the type of the default value, and can be a NSString, NSNumber or a Boolean casted to a NSNumber.

For example, using a variable of type NSNumber:

let tlVar = TaplyticsVar.async(name: "numVar", defaultValue: 1 as NSNumber) { (value) in 
     // Update your interface/functionality with new value.
}
// In your Interface create a strong reference to the variable
@property (nonatomic, strong) TaplyticsVar* tlVar;

// Using the variable in your code:
self.tlVar = [TaplyticsVar taplyticsVarWithName:@"numVar" defaultValue:@(1) updatedBlock:^(NSObject *value) {
    if (value) {
        NSNumber* num = (NSNumber*)value;
    }
}];

When the variable's value has been updated, the updated block will be called with that updated value. Note that we only store a weak reference to your variables, for the updated block to work correctly you will need to store a strong reference to the variable object.

Note: Default values for dynamic variables cannot be NULL. NULL values may cause default to trigger in all scenarios


Testing Dynamic Variables

When testing dynamic variables in live update mode you can change the values on the fly via the Taplytics interface and you can switch variations with the shake menu on the device.

Important Note: When testing synchronous dynamic variables you must initialize the variable again to see the new value, as there are no callbacks which occur when the variable is updated with a new value.

This can be achieved by using a new session callback (version 3.0.0 and above) or properties loaded callback (under version 3.0.0). Here is an example for updating a label:

Taplytics.newSessionCallback { (loaded) in
    guard loaded, let label = self.label else {
        return
    }

    let stringVar = TaplyticsVar.sync(name: "stringVar", defaultValue: "string" as NSString)
    if let stringValue = stringVar.value as? String {
        label.text = stringValue
    }
}
__weak id weakSelf = self;
[Taplytics newSessionCallback:^(BOOL loaded) {
    TaplyticsVar* var = [TaplyticsVar taplyticsSyncVarWithName:@"stringVar" defaultValue:@"defaultValue"];
    if (weakSelf && weakSelf.label) {
        weakSelf.label.text = var.value;
    }
}];

Testing Specific Experiments

To test/QA specific experiment and variation combinations use the TaplyticsOptionTestExperiments start option with a NSDictionary containing keys of the experiment names, and values of variation names (or baseline).

Taplytics.startAPIKey("SDK_KEY", options: [
    TaplyticsOptionTestExperiments: [
        "Experiment 1": "Variation 1",
        "Experiment 2": "baseline"
    ]
])
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{
    TaplyticsOptionTestExperiments: @{
        @"Experiment 1": @"Variation 1",
        @"Experiment 2": @"baseline"
    }
}];

List Current Variables

If you would like to see which variables are currently available within the session, there exists a getAllVariables method which will return an array of dictionaries.

🚧

Warning - Debug Use Only

This method is for debugging purposes only. Please do not use this method as a means to retrieve variable values for use.

Example:

Taplytics.getAllVariables { variables in 
    print("All Variables: \(variables)")
}
[Taplytics getAllVariables:^(NSArray<NSDictionary *> * _Nonnull variables) {
    // Returns all variables
}];

The returned array of dictionaries contains the following properties:

[
   [
      AnyHashable("name"): stringtest, 
      AnyHashable("_id"): "id_value", 
      AnyHashable("variableType"): String, 
      AnyHashable("value"): VARIATION 1]
   ]
]
Variables: (
  {
    "_id" = "id_value";
    name = "Variable Name";
    value = "";
    variableType = String;
  }
)

NOTE: This function runs asynchronously, as it waits for the updated properties to load from Taplytics servers.


List Running Experiments

If you would like to see which variations and experiments are running on a given device, there exists a getRunningExperimentsAndVariations() function which provides a callback with the current experiments and their running variation. An example:

Taplytics.getRunningExperimentsAndVariations { (experimentsAndVariations) in
    // For example experimentsAndVariations will contain:
    // [
    //    "Experiment 1": "baseline",
    //    "Experiment 2": "Variation 1",
    // ]
}
[Taplytics getRunningExperimentsAndVariations:^(NSDictionary *experimentsAndVariations) {
    // For example experimentsAndVariations will contain:
    // @{
    //     @"Experiment 1": @"baseline",
    //     @"Experiment 2": @"Variation 1"
    // };
}];

Note: The block can return asynchronously once Taplytics properties have loaded. The block will return a NSDictionary with experiment names as the key value, and variation names as the value.


Delay Load for First-view Experiments

On the first launch of your app, the Taplytics SDK will show your iOS launch image up to a maximum 4 seconds while it downloads properties from Taplytics servers. This delay will enable you to run experiments in the first view of your app.

If you would like to disable showing the launch image:

Taplytics.startAPIKey("SDK_KEY", options: [TaplyticsOptionDelayLoad: 0])
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{TaplyticsOptionDelayLoad:@0}];

Or increase the maximum wait time to 10 seconds:

Taplytics.startAPIKey("SDK_KEY", options: [TaplyticsOptionDelayLoad: 10])
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{TaplyticsOptionDelayLoad:@10}];

Feature Flags

Taplytics feature flags operate in synchronous mode. Synchronous feature flags are guaranteed to have the same value for the entire session and will have that value immediately after construction.

if (Taplytics.featureFlagEnabled(key: "featureFlagKey")) {
    // Put feature code here, or launch feature from here
}
if ([Taplytics featureFlagEnabled:@"featureFlagKey"]) {
    // Put feature code here, or launch feature from here
}

Due to the synchronous nature of feature flags, if it is used before the feature flags have been loaded from Taplytics servers (for example on the first launch of your app), it will default to as if the feature flag is not present. In order to prevent this you can ensure that the feature flags are loaded before using the feature flag. This can be done using the newSessionCallback: method, as an example:

// New as of version 3.0.0
Taplytics.newSessionCallback { (loaded) in
    if (Taplytics.featureFlagEnabled("featureFlagKey")) {
        // Put feature code here, or launch feature from here
    }
}

// Deprecated as of version 3.0.0
Taplytics.propertiesLoadedCallback { (loaded) in
    if (Taplytics.featureFlagEnabled("featureFlagKey")) {
        // Put feature code here, or launch feature from here
    }
}
// New as of version 3.0.0
[Taplytics newSessionCallback:^(BOOL loaded) {
    if ([Taplytics featureFlagEnabled:@"featureFlagKey"]) {
        // Put feature code here, or launch feature from here
    }
}];

// Deprecated as of version 3.0.0
[Taplytics propertiesLoadedCallback:^(BOOL loaded) {
    if ([Taplytics featureFlagEnabled:@"featureFlagKey"]) {
        // Put feature code here, or launch feature from here
    }
}];

Setting a Default value

As of version 2.51.0, we support including an optional default value that can be set to true or false in the case where the user does not have the feature flag enabled. If unprovided, the default values will be false.

WARNING: Setting the Feature Flag default to true means that users will always receive the Feature Flag, even when it has been disabled.

if (Taplytics.featureFlagEnabled(key: "featureFlagKey", defaultValue: true)) {
    // Put feature code here, or launch feature from here
}
if ([Taplytics featureFlagEnabled:@"featureFlagKey" defaultValue:YES]) {
    // Put feature code here, or launch feature from here
}

List Running Feature Flags

If you would like to see which feature flags are running on a given device, there exists a getRunningFeatureFlags() function which provides a callback with the current experiments and their running variation. An example:

Taplytics.getRunningFeatureFlags { (featureFlags) in
    // For example featureFlags will contain:
    // [
    //     @"Feature Flag 1's Name": @"Feature Flag 1's Key",
    //     @"Feature Flag 2's Name": @"Feature Flag 2's Key"
    // ]
}
[Taplytics getRunningFeatureFlags:^(NSDictionary *featureFlags) {
    // For example featureFlags will contain:
    // @{
    //     @"Feature Flag 1's Name": @"Feature Flag 1's Key",
    //     @"Feature Flag 2's Name": @"Feature Flag 2's Key"
    // };
}];

Note: The block can return asynchronously once Taplytics properties have loaded. The block will return a NSDictionary with feature flag names as the key value, and feature flag keys as the value.


Sessions

Taplytics automatically tracks sessions for you. The Taplytics SDK keeps track of the last activity timestamp in your app (app activity is considered a view change, button click, or Taplytics event logged), and when your app returns from background. If the time since last activity is greater than 10 minutes we create a new session for you. Similarly, if the app is entirely force closed, the next time the app is opened, it will be considered a new session.

Background Session Time

By default, Taplytics defines a session as when a user is using the app with less than 10 minutes of inactivity. If you would like the session background time something other than 10 minutes you can set it as a start option:

Taplytics.startAPIKey("SDK_KEY", options: [TaplyticsOptionSessionBackgroundTime: 10])
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{TaplyticsOptionSessionBackgroundTime: @10}];

Manually starting a new session

To manually force a new user session (ex: A user has logged in / out), there exists a startNewSession method.

If there is an internet connection, a new session will be created, and new experiments/variations will be fetched from Taplytics if they exist.

It can be used as follows:

Taplytics.startNewSession { (success) in
    // New session here! Success will be false if this failed.
}
[Taplytics startNewSession:^(BOOL success) {
    // New session here! Success will be false if this failed.
}];

Shake Menu

The shake menu allows you to QA Experiments, different Variations and Feature Flags on your device on the fly. You can also view and copy your push token if available.

Use the shake functionality to open it, or call it in code like so, optionally passing in a view controller to define where the shake menu should be presented:

Taplytics.showMenu(viewController)
[Taplytics showMenu:viewController];

Push Notifications

Setting up Push Notifications using Taplytics is simple. Follow the steps below to get started.

First and foremost, ensure that you have setup your Apple Push Certificates as per these docs.


Implementing Push Notification

You'll need to implement the UNUserNotificationCenterDelegate to allow Taplytics and iOS to accept Push Notifications. You will need to change your UIApplicationDelegate header file to look something like the following:

import UIKit
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    ...
}
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>

@end

You will also need to add the following methods to your UIApplicationDelegate

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // "userInfo" will give you the notification information
    completionHandler(UIBackgroundFetchResult.noData)
}

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // "notification.request.content.userInfo" will give you the notification information
    completionHandler(UNNotificationPresentationOptions.badge)
}

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    // "response.notification.request.content.userInfo" will give you the notification information
    completionHandler()
}
// Implement these methods for Taplytics Push Notifications
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
}

// Method will be called when the app recieves the push notification
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
   // "userInfo" will give you the notification information
   completionHandler(UIBackgroundFetchResultNoData);
}

// Method will be called if the app is open when it recieves the push notification
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
   // "notification.request.content.userInfo" will give you the notification information
   completionHandler(UNNotificationPresentationOptionBadge);
}

// Method will be called if the user opens the push notification
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
    // "response.notification.request.content.userInfo" will give you the notification information
    completionHandler();
}

Then in your didFinishLaunchingWithOptions, call Taplytics.registerPushNotifications.

TaplyticsDisablePushNotificationSW

If you have the TaplyticsDisablePushNotificationSW option enabled, you'll need to implement some additional methods to implement push properly:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  Taplytics.registerPushToken(deviceToken)
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // track when push is received
  	Taplytics.trackPushReceived(userInfo)
    completionHandler(UIBackgroundFetchResult.noData)
}

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // "notification.request.content.userInfo" will give you the notification information
    completionHandler(UNNotificationPresentationOptions.badge)
}

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    // track when push is opened
  	if (response.actionIdentifier == UNNotificationDefaultActionIdentifier) {
      // push open
      Taplytics.trackPushOpen(response.notification)
    }
    completionHandler()
}

Register for Push Notifications

You'll also need to register for push notifications with iOS. When you do so, iOS will ask your users for permission and enable the ability to receive notifications to that device.

You'll need to enable a few capabilities on your app. Go into your Project Settings and find your project under Targets. Select the Capabilities tab and turn on Push Notifications and Background Modes. Under Background Modes, enable Remote Notifications.

3314

If you are not already registering for push notifications all you have to do is call registerPushNotifications: on Taplytics, and we take care of all the rest!

Please Note that calling this function will show the permission dialog to the user.

Taplytics.registerPushNotifications()
[Taplytics registerPushNotifications];

Register for Location Permissions (Optional)

For automated push campaigns using location based regions you will need to add the CoreLocation framework to your app, and request location permissions from your users. Taplytics will automatically update and manage the monitored regions on your device for your automated push campaigns.

You can handle asking for location permissions yourself, or you can use our provided method as seen below. But make sure that you request AuthorizedAlways permissions so that we can set regions.

Taplytics.registerLocationAccess()
// We will request AuthorizedAlways access to be able to set monitored regions
[Taplytics registerLocationAccess];

In order to allow the iOS location manager to successfully display a location request dialog to the user, the following properties must be added to the application's Plist settings:

NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription

These values will be used by the OS to display the reason for requesting location access.


Receiving Push Notifications

To be able to send your users Push Notifications, we'll need you to upload your Apple Push certificates. Please follow this guide to do so.


Environments (Development and Production)

In order to separate your development devices and production devices, Taplytics automatically buckets devices into Development and Production buckets. We do this by looking at the provisioning profile that the app was built with.

Development Bucketing

If the app is built using a Development Provisioning Profile, we bucket the device into the Development environment. You can change this by forcing an environment.

All devices that fall into the Development environment, Taplytics will use the Development Push Notification Certificate to attempt to send push notifications to your devices.

Production Bucketing

If the app is built using a Distribution Provisioning Profile, we bucket the device into the Production environment. Note that this means that if you're distributing Ad-Hoc or Enterprise builds through a service like Testflight or Hockeyapp, then all those devices running those builds will fall into the Production environment. You can change this by forcing an environment.

All devices that fall into the Production environment, Taplytics will use the Production Push Notification Certificate to attempt to send push notifications to your devices.

Forcing an Environment

You can use the options attribute when you start Taplytics in order to force the environment that your devices are bucketed into. This can be especially useful for any Ad-Hoc and Enterprise distribution you might be doing.

Here's how you can achieve setting the Push environment:

// To bucket everyone into Production:
Taplytics.startAPIKey("SDK_KEY", options: ["pushSandbox": 0])

// To bucket everything into Development:
Taplytics.startAPIKey("SDK_KEY", options: ["pushSandbox": 1])
// To bucket everyone into Production:
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{@"pushSandbox":@0}];

// To bucket everything into Development:
[Taplytics startTaplyticsAPIKey:@"SDK_KEY" options:@{@"pushSandbox":@1}];

Resetting Users

If you're using our User Attributes feature, you can easily disconnect a user from a device when they log out. This will prevent our system from sending push notifications to that device. It is especially important to reset the user when your push campaigns are targeted to a specific user. You can do this by calling our resetUser: function:

Taplytics.resetUser {
  // Finished user reset
}
[Taplytics resetUser:^{
    // Finished User Reset
}];

Rich Push Notifications (iOS 10+)

Implementing rich push notification support can help improve user engagement with your push notifications with image content attached. Rich push notifications make use of Notification Service Extension on iOS 10+ to display images attached to the push notifications. We currently support JPEG and PNG images sent through the Taplytics dashboard or API.

The max image size that can be uploaded is 10mb. Note that images are not downscaled and if an image is sent, the full file size of the crop will be used.

Create a Notification Service Extension

You'll need to add a Notification Service Extension to your app which is a small extension to your app that downloads the image attached to the notification and displays it as part of the notification.

To create the extension open File > New > Target in Xcode, select Notification Service Extension, then name your service extension and create it with language Swift.

1466

Once you've created the Notification Service Extension you should have a file called NotificationService.swift, open that up and edit its didReceiveRequestWithContentHandler method with the following code:

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

    // look for existance of taplytics data with image_url
    if let tlData = request.content.userInfo["taplytics"] as? [String: Any], let imageUrl = tlData["image_url"] as? String, let url = URL(string: imageUrl) {
        URLSession.shared.downloadTask(with: url) { (location, response, error) in
            if let location = location {
                // get path in temp directory for file
                let tempFileURL = URL(string: "file://".appending(NSTemporaryDirectory()).appending(url.lastPathComponent))!

                var attachment: UNNotificationAttachment?
                do {
                    // move file into temp directory to be displayed by Notification Service Extension
                    if (FileManager.default.fileExists(atPath: tempFileURL.relativePath)) {
                        try FileManager.default.removeItem(at: tempFileURL)
                    }
                    try FileManager.default.moveItem(at: location, to: tempFileURL)

                    // generate image attachment
                    attachment = try UNNotificationAttachment(identifier: "tl_image", url: tempFileURL)
                } catch let error {
                    print("Error: \(error)")
                }

                // Add the attachment to the notification content
                if let attachment = attachment {
                    self.bestAttemptContent?.attachments = [attachment]
                }
            }

            // render notification
            self.contentHandler!(self.bestAttemptContent!)
        }.resume()
    } else {
        // If there is no image payload render the notification as a normal notification.
        self.contentHandler!(self.bestAttemptContent!)
    }
}

What this code is doing is looking for any data attached to the push payload under a taplytics object, and specifically looking for a taplytics.image_url url to download an image from, which will then start a downloadTask for that url. Once the image is downloaded it will move the image to the Extension's temp directory and add the image as a notification attachment to the push, and finally render the notification to display it.

Any push notifications sent without the image url attached to its data will display as normal by iOS.


Push Notification Payload

If you are handling push notifications with custom payloads, the custom data key/values will be added to the base object as seen below:

{
    "custom_data_key": "custom_data_value",
    "aps": {
        "alert": "Test Push",
        "badge": 1,
        "sound": "default",
        "tl_id": "",
        "content-available": "1",
        "mutable-content": "1"
    },
    "taplytics": {
        "image_url": ""
    }
}

Troubleshooting

Here are some of the common errors we've run into when installing the Taplytics iOS SDK and the fixes to go with them. We hope it helps keep the installation smooth, if you feel like you need a hand, please don't hesitate to reach out. We'd love to hear from you.


"OBJC_CLASS$_Taplytics" not found

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_Taplytics", referenced from:
      objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

If you encounter the above error when attempting to build and run your app in XCode, please make sure Taplytics.framework is in the Link Binary With Libraries.

To navigate to the correct menu: Project > Build Phases > Link Binary With Libraries


"_utf8_nextCharSafeBody" build error

Undefined symbols for architecture x86_64:
  "_utf8_nextCharSafeBody", referenced from:
      -[TL_SRWebSocket _innerPumpScanner] in Taplytics(TL_SRWebSocket.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

If you encounter this error when attempting to build and run your app in XCode, the entry for libicucore.dylib might be missing, please make sure that it has been added to your Link Binary With Libraries list.

To navigate to the correct menu: Project > Build Phases > Link Binary With Libraries


Terminating app due to uncaught exception 'NSInvalidArgumentException'

...
libc++abi.dylib: terminating with uncaught exception of type NSException

If you encounter this error when attempting to build and run your app in XCode, the -ObjC flag is missing under Other Linker Flags.

To navigate to the correct menu: Project > Build Settings > Other Linker Flags = "-ObjC"


"OTHER_LDFLAGS" Error

If you're running into an "OTHER_LDFLAGS" error when trying to build your podfile using $pod install, we have a simple fix for you!

Here's an example of the error:

[!] The target `Taplytics Example App [Debug]` overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Pods.xcconfig'.
  - Use the `$(inherited)` flag, or
  - Remove the build settings from the target.
[!] The target `Taplytics Example App [Debug - Release]` overrides the `OTHER_LDFLAGS` build setting defined in `Pods/Pods.xcconfig'.
  - Use the `$(inherited)` flag, or
  - Remove the build settings from the target.

The Fix:
In XCode, go to "Build Settings" and find the "Other Linker Flags" setting under "Linking". The entry for this setting should be bolded.

Option A
Select the value for the "Other Linker Flags" setting and hit backspace. This should remove the bolded value, and a list of non-bolded values should appear. These are all the flags from the CocoaPod.

Option B
If you need to have your own flags declared, include $(inherited) in the list of flags. This should append all the flags defined in the CocoaPod to your list of flags.

The Additional Fix:
If the fix above still doesn't address the compilation error, please try clearing the bolded values or add $(inherited) to the following Build Settings: Framework Search Paths Header Search Paths