iOS SDK integration for developers

  • Advertisers
  • Developers

Integrate AppsFlyer SDK into your iOS app, record installs, updates and in-app events. Evaluate user engagement.

ios.pngSDK Version: 4.10.4 (Release notes)

1. Overview

Using AppsFlyer SDK you can record installs, updates, sessions, and additional in-app events. Using in-app events you can record events like in-app purchases and game levels. Use this information to evaluate ROI and user engagement levels. The iOS SDK is compatible with all iOS devices (iPhone, iPod, iPad) using iOS 6.0 and later.

 Note

AppsFlyer SDK is fully compliant with Apple IPv6 DNS64/NAT64 networks. For more information, click here.

 Important!

AppsFlyer SDK makes use of NSUserDefaults class. Clearing all values from NSUserDefaults may cause attribution issues.

 Tip

  • In order to implement basic SDK integration that is, install attribution only, it is mandatory to complete the procedures in sections 2 and 3 in this document.
  • It is recommended that you read the Recording in-app events section as an aid to implementation
  • The rest of the described features described, are optional and implementing them will depend on your app's business logic. For example, recording revenue or getting the conversion data on first launch may be vital for your app's flow

2. Quick start

2.1 Download and add AppsFlyer SDK to xcode

CocoaPodsCarthageStatic FrameworkStatic Lib
  1. Make sure you have downloaded and installed the latest version of CocoaPods.
  2. Add the following row to your Podfile:
    pod 'AppsFlyerFramework'
  3. Run pod install
  4. Make sure you use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file, from here on out.

AppsFlyer's SDK uses the following native frameworks:

AdSupport.framework
This framework is required to collect the IDFA from devices.
Without IDFA you cannot attribute installs to Facebook Ads, Twitter, Google ads and other networks.
iAd.framework
This framework is required to record and measure the performance of Apple Search Ads in your app

2.2 Configure integration in-app delegate

SwiftObjective-C

In AppDelegate.swift, do the following:

  1. Import AppsFlyerLib
  2. Add AppsFlyerTrackerDelegate to the class declaration
import AppsFlyerLib
class AppDelegate: UIResponder, UIApplicationDelegate, AppsFlyerTrackerDelegate {
   // your code here
}

3. SDK initialization

Initialize the SDK in the didFinishLaunchingWithOptions method with your app ID taken from iTunes Connect and your AppsFlyer dev key. Also, add the API methods to handle conversion data and deep linking.

Note that you need to set the app ID here with digits only, without the "id" prefix.

SwiftObjective-C
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        AppsFlyerTracker.shared().appsFlyerDevKey = ""
        AppsFlyerTracker.shared().appleAppID = ""
        
        AppsFlyerTracker.shared().delegate = self
        
        /* Set isDebug to true to see AppsFlyer debug logs */
        AppsFlyerTracker.shared().isDebug = true

        return true
    }

// rest of your code, methods such as applicationWillResignActive, applicationDidEnterBackground etc.

//get conversion data and deep linking

 func onConversionDataReceived(_ installData: [AnyHashable: Any]) {
  //Handle Conversion Data (Deferred Deep Link)
  }
  
  func onConversionDataRequestFailure(_ error: Error?) {
    //    print("\(error)")
  }
  
  func onAppOpenAttribution(_ attributionData: [AnyHashable: Any]) {
    //Handle Deep Link Data
    
  }
  
  func onAppOpenAttributionFailure(_ error: Error?) {
  }
// Reports app open from a Universal Link for iOS 9 or later
  func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
    AppsFlyerTracker.shared().continue(userActivity, restorationHandler: restorationHandler)
    return true
  }

  // Reports app open from deep link from apps which do not support Universal Links (Twitter) and for iOS8 and below
  func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
    AppsFlyerTracker.shared().handleOpen(url, sourceApplication: sourceApplication, withAnnotation: annotation)
    return true
  }

  // Reports app open from deep link for iOS 10 or later
  func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    AppsFlyerTracker.shared().handleOpen(url, options: options)
    return true
  }

For Swift 4.2 and above, use the following code for the continue userActivity method:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
	AppsFlyerTracker.shared().continue(userActivity, restorationHandler: nil)
	return true
}

 Note

If isDebug=true is set, AppsFlyer SDK logs are shown in the xCode Console

 

  • Add the following code at the applicationDidBecomeActive function:
SwiftObjective-C
func applicationDidBecomeActive(application: UIApplication) { 
// attribute Installs, updates & sessions(app opens) 
// (You must include this API to enable SDK functions) 
AppsFlyerTracker.shared().trackAppLaunch() 
// your other code here.... }

Verifying that track app launch is successful

You can verify that the request to track app launch is successful by implementing trackAppLaunchWithCompletionHandler. You can then apply logic to handle success or failure of tracking app launch.

Example

[[AppsFlyerTracker sharedTracker] trackAppLaunchWithCompletionHandler:^(NSDictionary<NSString *,id> *dictionary, NSError *error) {
        if (error) {
            NSLog(@"%@", error);
            return;
        }
        if (dictionary) {
            NSLog(@"%@", dictionary);
            return;
        }
        [NSException exceptionWithName:@"fatalError" reason:nil userInfo:nil];
    }];

4. Recording in-app events

In-App Events provide insights on what is happening in your app. It is recommended to take the time and define the events you want to measure to allow you to measure ROI (Return on Investment) and LTV (Lifetime Value).

Recording in-app events is performed by calling trackEvent with event name and value parameters. See In-App Events for more details.

An in-app event name must not be longer than 45 characters. Event names with more than 45 characters do not appear in the dashboard, but only in the raw Data, Pull and Push APIs.

Example: Purchase In-App Event

SwiftObjective-C
AppsFlyerTracker.shared().trackEvent(AFEventPurchase,
  withValues: [
     AFEventParamRevenue: "1200",
     AFEventParamContent: "shoes",
     AFEventParamContentId: "123"
]);

swift-record-in-app-events.png

This generates an af_purchase event type (using the AFEventPurchase constant) with the following event values: {af_revenue: 200 , af_currency: "USD", af_quantity: 2, af_content_id: "092" af_receipt_id: "9277"}

 Note

AppsFlyer supports non-English characters with in-app events, or with any other SDK API, starting from iOS SDK version 4.8.1.

The event value dictionary passed to the event SDK must be valid for JSON conversion by NSJSONSerialization. For more information, see here.

For revenue, do not add any currency symbols as these are not recognized.

 Usage Example

SwiftObjective C
AppsFlyerTracker.shared().trackEvent(AFEventPurchase,
withValues: [
  AFEventParamRevenue: @1200,
  AFEventParamCurrency : @"JPY"
]); 

Verifying in-app event recording

You can verify that recording in-app events is successful or not by implementing the completionHandler. You can then apply logic to handle success or failure of recording events.

Example

[[AppsFlyerTracker sharedTracker] trackEventWithEventName:AFEventPurchase
	eventValues:@{AFEventParamRevenue: @"1200",
					AFEventParamContent: @"shoes",
					AFEventParamContentId: @"123"}
	completionHandler:^(NSDictionary<NSString *,id> *dictionary, NSError *error) {
	if (error) {
		NSLog(@"%@", error);
		return;
	}
	if (dictionary) {
		NSLog(@"%@", dictionary);
		return;
	}
	[NSException exceptionWithName:@"fatalError" reason:nil userInfo:nil];

5. Performing deep linking

 Tip

We highly recommend having deep linking integrated in your app. Deep Linking is a crucial part of retargeting campaigns and it is highly recommended to use when running retargeting campaigns.

iOS9 and above requires your app to support Universal Links. For more details, click here.


To handle deep linking, add the following code in your app (in the app delegate class). The methods in this code allow you to get deep linking data. With deep linking data you can redirect users to the relevant app activity and serve them with the relevant content.

SwiftObjective-C

 func onConversionDataReceived(_ installData: [AnyHashable: Any]) {
  //Handle Conversion Data (Deferred Deep Link)
  }
  
  func onConversionDataRequestFailure(_ error: Error?) {
    //    print("\(error)")
  }
  
  func onAppOpenAttribution(_ attributionData: [AnyHashable: Any]) {
    //Handle Deep Link Data
    
  }
  
  func onAppOpenAttributionFailure(_ error: Error?) {
  }
// Reports app open from a Universal Link for iOS 9 or later
  func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
    AppsFlyerTracker.shared().continue(userActivity, restorationHandler: restorationHandler)
    return true
  }

  // Reports app open from deep link from apps which do not support Universal Links (Twitter) and for iOS8 and below
  func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
    AppsFlyerTracker.shared().handleOpen(url, sourceApplication: sourceApplication, withAnnotation: annotation)
    return true
  }

  // Reports app open from deep link for iOS 10 or later
  func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    AppsFlyerTracker.shared().handleOpen(url, options: options)
    return true
  }
  

For Swift 4.2 and above, use the following code for the continue userActivity method:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
	AppsFlyerTracker.shared().continue(userActivity, restorationHandler: nil)
	return true
}

6. Recording revenue

Use the af_revenue (AFEventParamRevenue) event parameter to count revenue as part of an in-app event. You can populate it with any numeric value, positive or negative.

 Note

AFEventParamRevenue (equivalent to using af_revenue) is the ONLY event parameter that is counted on AppsFlyer as real revenue on the raw data and dashboard. For more details please click here.


Example: Revenue In-App Event

SwiftObjective-C
AppsFlyerTracker.shared().trackEvent(AFEventPurchase, 
withValues: [
	AFEventParamContentId:"1234567",
	AFEventParamContentType : "category_a",
	AFEventParamRevenue: 1.99,
	AFEventParamCurrency:"USD"
]);

 Important!

Do NOT format the revenue value in any way. It should not contain comma separators, currency sign, or text. A revenue event should be similar to 1234.56, for example.

Recording negative revenue

If you need to record negative revenue for example when a user cancels a purchase or receives a refund, you can send negative revenue.

SwiftObjective-C
AppsFlyerTracker.shared().trackEvent("cancel_purchase", 
withValues: [
	AFEventParamContentId:"1234567",
	AFEventParamContentType : "category_a",
	AFEventParamRevenue: -1.99,
	AFEventParamCurrency:"USD"
]);

 Note

Notice the following in the code above:

  1. The revenue value is preceded by a minus sign
  2. The event name has a unique value of "cancel_purchase" - to allow you to identify negative revenue events in the dashboard and raw data reports

7. Get conversion data

AppsFlyer allows you to access the user attribution data in real-time for every new install, directly from the SDK level. By doing this you can serve users with personalized content or send them to specific activities within the app, which can greatly enhance their engagement with your app.

For more information regarding this advanced functionality, click here.

8. User identifiers

There are a number of options for retrieving user identifiers:

Get AppsFlyer device ID

AppsFlyer's unique device ID is created for every new install of an app. You can obtain it using the following code:

SwiftObjective-C
let appsflyerId = AppsFlyerTracker.shared().getAppsFlyerUID()

Set customer user ID

Setting your own customer ID enables you to cross-reference your own unique ID with AppsFlyer’s unique ID and the other device IDs. The ID is available in AppsFlyer raw data reports and postback APIs for cross-referencing with your internal IDs.

To set your Customer User ID:

SwiftObjective-C
AppsFlyerTracker.shared().customerUserID = "my user id"

 Important Note

It is recommended to set your Customer User ID as soon as possible as it is only associated to events reported after its setup. If setCustomerUserId is called before calling trackAppLaunch, the Customer User ID is visible in the raw export for installs and for events. If it is set after, only the value for events recorded is visible after calling this method.

Customer User ID can also be used to complete integrations with Analytics platforms such as Mixpanel and Swrve.

Getting Customer User ID:

To get the customer user ID retrieve it from the sharedTracker.

[AppsFlyerTracker sharedTracker].customerUserID

 Important!

Make sure to set the customer user ID each time the app is launched, before calling trackAppLaunch

For more information about the Customer User ID, click here.

IDFA and IDFV

AppsFlyer automatically collects the IDFA (ID For Advertisers) and IDFV (ID For Vendors) if AdSupport.framework is included in the app.

9. Optional features

Measure uninstalls

For Uninstall measurement, enable remote push notification on your app. See Apple's Remote Notification Programming Guide for more details.

Follow the iOS SDK integration instructions to complete the uninstall measurement feature setup.

Recording push notifications

To enable Tracking App Launches from push notifications, add the following code to your app delegate:

SwiftObjective-C
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
	AppsFlyerTracker.shared().handlePushNotification(userInfo)
}

The push message should have an af parameter with AppsFlyer attribution params.

Message Example:

{
  "aps":{
   "alert":"Push text",
   "sound":"default",
   "category":"REMINDER_CATEGORY"
  },
  "_p":123456,
  "payloadKey":"payloadValue",
  "af":{
   "pid":"swrve_int",
   "is_retargeting":"true",
   "c":"test_campaign"
  }
}

Cross promotion attribution

AppsFlyer allows you to attribute and record installs originating from a cross promotion of one of your apps from within the current app the user has. Cross promoting apps can be a major growth factor in driving additional installs for your apps.

For details, see the Cross Promotion Attribution article, here.

User invite attribution

AppsFlyer allows you to attribute and record installs originating from user invites within your app. Allowing your existing users to invite their friends and contacts as new users to your app can be a key growth factor for your app.
 
For details, see the User Invite Attribution article, here.

Set currency code

You can set a global currency code using the API below, in addition to specific currency codes that can be used as part of each in-app event sent to AppsFlyer. The global currency code is used when AFEventParamCurrency is not sent as part of an in-app event.

USD is the default value. You can find acceptable ISO currency codes here.

Use the following API to set the currency code:

public void currencyCode(String currencyCode);

Usage Example:

SwiftObjective-C
AppsFlyerTracker.shared().currencyCode = "USD"

In-app purchase validation

 Note

This function is supported for iOS7 and above.

AppsFlyer’s SDK can provide in‐app purchase server verification. To set receipt validation tracking you need to call the validateAndTrackInAppPurchase method inside the SKStoreKit’s completeTransaction: callback. This call automatically generates an af_purchase in‐app event.

- (void) validateAndTrackInAppPurchase:(NSString *) productIdentifier
price:(NSString *) price
currency:(NSString *) currency
transactionId:(NSString *) tranactionId
additionalParameters:(NSDictionary *) params
success:(void (^)(NSDictionary *response)) successBlock
failure:(void (^)(NSError *error, id reponse)) failedBlock;

 Note

The price parameter should contain the total revenue associated with the validated purchase event.


This call has two callback blocks, one for ‘success’ and one for ‘failure’ (for any reason, including validation fail). On success, a dictionary is returned with the receipt validation data provided by Apple’s servers.

 Important

For testing purposes, we recommend setting the useReceiptValidationSandbox flag to YES, as this redirects the requests to Apple sandbox servers.

#ifdef DEBUG
[AppsFlyerTracker sharedTracker].useReceiptValidationSandbox = YES;
#endif

 

 Example

Objective-CSwift
[[AppsFlyerTracker sharedTracker] validateAndTrackInAppPurchase:@"ProductIdentifier" price:@"price"
    currency:@"USD"
    transactionId:@"transactionID"
    additionalParameters:@{@"test": @"val" , @"test1" : @"val 1"}
    success:^(NSDictionary *result){
      NSLog(@"Purchase succeeded And verified!!! response: %@", result[@"receipt"]);
    } failure:^(NSError *error, id response) {
      NSLog(@"response = %@", response);
      if([response isKindOfClass:[NSDictionary class]]) {
        if([response[@"status"] isEqualToString:@"in_app_arr_empty"]){
          // retry with 'SKReceiptRefreshRequest' because
          // Apple has returned an empty response
          // <YOUR CODE HERE>
        }

      } else {
        //handle other errors
        return;
      }
  }];

 Important!

When AppsFlyer validates a purchase against Apple servers, if the response contains an empty in-app array, AppsFlyer returns {"status":"in_app_arr_empty"} to the error callback.

If you receive this flag, you must restore the purchased product with Apple by sending a receipt refresh request. For more information see Apple's documentation.

After refreshing the receipt, call validateAndTrackInAppPurchase once more.

See the code above to learn how to handle this error.

For a list of possible return values for validating receipts, please refer to Apple's documentation here.

Anonymize

AppsFlyer provides you with a method to anonymize specific user identifiers in AppsFlyer analytics. This method complies with the latest privacy requirements and complies with Facebook data and privacy policies. Default is NO, meaning no anonymization is performed by default.

Use this API during the SDK Initialization to explicitly anonymize a user's installs, events and sessions:

SwiftObjective-C
AppsFlyerTracker.shared().deviceTrackingDisabled = true

Tracking can be restarted by calling deviceTrackingDisabled again set to false.

 Warning

Anonymizing users SEVERELY impacts your attribution information.
Use this option ONLY for regions which legally prevent you from collecting your users' information.

Custom time between sessions

By default, at least 5 seconds must lapse between 2 app launches to count as separate 2 sessions (more about counting sessions).
However, you can use the following API to set your custom value for the minimum required time between sessions:

SwiftObjective-C
AppsFlyerTracker.shared().minTimeBetweenSessions = <your_custom_time_in_seconds>

Note that setting a high value to the custom time between launches may badly impact APIs relying on sessions data, such as deep linking.

Background sessions for utility apps

Unavailable in iOS.
 

iOS app extensions and watchkit

The app extension requires permissions to use the Internet:

  1. Go to your app extension's info.plist file
  2. In the NSExtension / NSExtensionAttributes set the RequestsOpenAccess flag to YES.

Add the following code to the app extension's UIViewController viewDidLoad:

SwiftObjective-C
override func viewDidLoad() {  
    super.viewDidLoad()
    AppsFlyerTracker.shared().appsFlyerDevKey = "MY_APPSFLYER_KEY"

    // MY_APP_ID below stands for you app ID on iTunes Connect. Should be 9 or 10 digits.
    AppsFlyerTracker.shared().appleAppID = "MY_APP_ID"
        
    AppsFlyerTracker.sharedTracker().trackAppLaunch()
  }

To receive attribution data on the app extension, follow the instructions here and implement it on the UIViewController of your app instead of in the AppDelegate.

Opt-out

In some extreme cases you might want to shut down all SDK functions due to legal and privacy compliance. This can be achieved with the isStopTracking API. Once this API is invoked, our SDK no longer communicates with our servers and stops functioning.

There are several different scenarios for user opt-out. We highly recommend following the exact instructions for the scenario, that is relevant for your app.

 Warning

Use this API only in cases where you want to fully ignore this user from any and all install attribution and event recording. Using this API SEVERELY impacts your attribution, data collection and deep linking mechanism.

SwiftObjective-C
AppsFlyerTracker.shared().isStopTracking = true

 Important

Do not call trackAppLaunch if isStopTracking is set to true

Delay SDK init for customer user ID

It is possible to delay the SDK Initialization until the customerUserID is set. This makes sure that the SDK doesn't begin functioning until the customerUserID is provided. When you delay SDK initialization, all in-app events and any other SDK API calls are discarded, until the customerUserID is provided and recorded.

You can add logic to the applicationDidBecomeActive iOS API to delay SDK initialization until customer user ID is set. See examples below:

- (void)applicationDidBecomeActive:(UIApplication *)application {
    NSString *customUserId = [[NSUserDefaults standardUserDefaults] stringForKey:@"customerUserId"]; // Your custom logic of retrieving CUID
    if (customUserId != nil && ![customUserId  isEqual: @""]) {
        [AppsFlyerTracker sharedTracker].customerUserID = customUserId; // Set CUID in AppsFlyer SDK for this session
        [[AppsFlyerTracker sharedTracker] trackAppLaunch]; // Track App Launch
    }
}
func applicationDidBecomeActive(_ application: UIApplication) {
        let customUserId = UserDefaults.standard.string(forKey: "customUserId")  //  your logic to retrieve CUID
        if(customUserId != nil && customUserId != ""){
            AppsFlyerTracker.shared().customerUserID = customUserId // Set CUID in AppsFlyer SDK for this session
            AppsFlyerTracker.shared().trackAppLaunch() // Track App Launch
        }
    }

Collect device name

AppsFlyer SDK allows you to collect Device Name for your internal analysis. By default, this capability is turned off. To turn it on use the following API:

SwiftObjective-C
AppsFlyerTracker.shared().shouldCollectDeviceName = false

 Important

Device Name might be considered Personal Data in certain regions. Only collect this information if you know you are legally allowed to and have received the user's explicit consent to do so.

Setting additional custom data

The setAdditionalData API is required to integrate on the SDK level with several external partner platforms, including Segment, Adobe and Urban Airship. Use this API only if the integration article of the platform specifically states setAdditionalData API is needed.
The following is a code example for implementing setAdditionalData on iOS for Objective-C or Swift

Objective-CSwift
NSDictionary* CustomDataMap = [[NSDictionary alloc] initWithObjectsAndKeys:@"value_of_param_1", @"custom_param_1", nil];
  
[[AppsFlyerTracker sharedTracker] setAdditionalData:CustomDataMap];

Resolving wrapped deep link URLs

If you are using OneLinks which support Universal Links and wrapping them with a 3rd Party Universal Link, you can use the setResolveDeepLinkURLs API to notify the AppsFlyer SDK which click domains that invoke the app should be resolved by the SDK and have the underlying OneLink extracted from them. This will allow you to maintain deep linking and attribution while wrapping the OneLink with a 3rd party Universal Link. Make sure to call this API before SDK initialization.

Objective-CSwift
[AppsFlyerTracker sharedTracker].resolveDeepLinkURLs = @[@"example.com",@"click.example.com"];

10. Testing your integration

For details on how to test your integration, click here.

Now you can start measuring results of the media sources you work with.

11. Submitting app to app store

You can find the instructions on submitting your app to the App Store here.

Was this article helpful?
10 out of 18 found this helpful

Comments

1 comment
  • Hi everyone!

    We want to update you that a new AppsFlyer iOS SDK has been released - version 4.10.3.

    This version supports iOS 13 updates, specifically push token retrieval for Uninstall Measurement.
    The new version also includes bugs fixes and improvements.

    We recommend that you update your app to use version 4.10.3.

    Thank you!

    0
    Comment actions Permalink

Please sign in to leave a comment.