Integrate iOS SDK 7

Initializing the iOS SDK

It's recommended to initialize the SDK in didFinishLaunchingWithOptions. This ensures the SDK can start in any scenario, including deep linking.

Step 1: Import dependencies
In your AppDelegate, import AppsFlyerLib:

// AppDelegate.h
#import <AppsFlyerLib/AppsFlyerLib.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@end
import UIKit
import AppsFlyerLib

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    // ...
}

Step 2: Initialize the SDK
In didFinishLaunchingWithOptions, initialize the SDK with your dev key and Apple App ID:

[[AppsFlyerLib shared] initWithDevKey:@"<YOUR_DEV_KEY>" appleAppId:@"<APPLE_APP_ID>"];
AppsFlyerLib.shared().initialize(devKey: "<YOUR_DEV_KEY>", appId: "<APPLE_APP_ID>")

Configuring the SDK with AppsFlyerLibConfig.plist

Optional

SDK V7 introduces an optional property list file for configuring SDK behavior without code. Add AppsFlyerLibConfig.plist to your app's main bundle. The SDK loads it automatically during initialization, before initialize(devKey:appId:) is called. If the file is missing, the SDK uses defaults. If a key is absent from the plist, its default applies. Programmatic API calls always override plist values.

Supported keys

Plist keyTypeDefaultProgrammatic equivalent
debug_modeBooleanfalseAppsFlyerLib.shared().isDebug = true
currency_codeString"USD"AppsFlyerLib.shared().currencyCode = "ILS"
disable_idfa_collectionBooleanfalseAppsFlyerLib.shared().disableAdvertisingIdentifier = true
disable_idfv_collectionBooleanfalseAppsFlyerLib.shared().disableIDFVCollection = true
disable_skadnetworkBooleanfalseAppsFlyerLib.shared().disableSKAdNetwork = true
min_time_between_sessionsInteger (seconds)30AppsFlyerLib.shared().minTimeBetweenSessions = 1
hostString""AppsFlyerLib.shared().setHost("prefix", hostName: "host.com")
prefixString""(set together with host)

Example: AppsFlyerLibConfig.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>debug_mode</key>
    <true/>
    <key>currency_code</key>
    <string>USD</string>
    <key>disable_idfa_collection</key>
    <false/>
    <key>disable_idfv_collection</key>
    <false/>
    <key>disable_skadnetwork</key>
    <false/>
    <key>min_time_between_sessions</key>
    <integer>30</integer>
    <key>host</key>
    <string></string>
    <key>prefix</key>
    <string></string>
</dict>
</plist>

Adding the file to Xcode

  1. Choose File → New → File and select Property List.
  2. Name it exactly AppsFlyerLibConfig (Xcode adds .plist).
  3. Add it to your app target, not the test target.
  4. Verify it appears under Build Phases → Copy Bundle Resources.
📘

Note

Any value set via the public API overwrites the corresponding plist value for the remainder of the process. On the next cold start, the plist value is loaded again.


Starting the iOS SDK

You control when the SDK sends its first session. Use registerSessionReadyListener to be notified when the SDK is ready, then call start when your app's conditions are met.

Without pre-conditions

Use this if your app has no pre-start conditions. If your app supports Universal Links, call handleLaunchOptions before registering the listener.

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [[AppsFlyerLib shared] initWithDevKey:@"<YOUR_DEV_KEY>" appleAppId:@"<APPLE_APP_ID>"];

    // Optional - only needed if supporting Universal Links
    [[AppsFlyerLib shared] handleLaunchOptions:launchOptions];

    [[AppsFlyerLib shared] registerSessionReadyListener:^{
        // Collect ATT here if required before start
        [[AppsFlyerLib shared] start];
    }];
    return YES;
}
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    AppsFlyerLib.shared().initialize(devKey: "<YOUR_DEV_KEY>", appId: "<APPLE_APP_ID>")

    // Optional - only needed if supporting Universal Links
    AppsFlyerLib.shared().handleLaunchOptions(launchOptions)

    AppsFlyerLib.shared().registerSessionReadyListener {
        // Collect ATT here if required before start
        AppsFlyerLib.shared().start()
    }
    return true
}

With pre-conditions

If start must wait for both SDK readiness and your consent flow (CMP, ATT, or a custom gate), use a lightweight coordinator. Each side sets a flag independently — the SDK when the listener fires, your app when the consent flow completes — and start is called only when both are set.

// AppDelegate.m
@interface AppDelegate ()
@property (nonatomic) BOOL consentGranted;
@property (nonatomic) BOOL sdkReady;
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [[AppsFlyerLib shared] initWithDevKey:@"<YOUR_DEV_KEY>" appleAppId:@"<APPLE_APP_ID>"];
    [[AppsFlyerLib shared] handleLaunchOptions:launchOptions];

    __weak typeof(self) weakSelf = self;
    [[AppsFlyerLib shared] registerSessionReadyListener:^{
        weakSelf.sdkReady = YES;
        [weakSelf startIfReady];
    }];

    // Trigger your CMP / ATT flow here.
    // When it completes, set:
    //   self.consentGranted = YES;
    //   [self startIfReady];

    return YES;
}

- (void)startIfReady {
    if (self.consentGranted && self.sdkReady) {
        [[AppsFlyerLib shared] start];
        self.sdkReady = NO; // prevent duplicate starts in the same cycle
    }
}

@end
class AppDelegate: UIResponder, UIApplicationDelegate {

    private var consentGranted = false
    private var sdkReady = false

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        AppsFlyerLib.shared().initialize(devKey: "<YOUR_DEV_KEY>", appId: "<APPLE_APP_ID>")
        AppsFlyerLib.shared().handleLaunchOptions(launchOptions)

        AppsFlyerLib.shared().registerSessionReadyListener { [weak self] in
            self?.sdkReady = true
            self?.startIfReady()
        }

        // Trigger your CMP / ATT flow here.
        // When it completes, call:
        //   consentGranted = true
        //   startIfReady()

        return true
    }

    private func startIfReady() {
        guard consentGranted, sdkReady else { return }
        AppsFlyerLib.shared().start()
        sdkReady = false
    }
}

SwiftUI — use @UIApplicationDelegateAdaptor to wire the same AppDelegate:

@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup { ContentView() }
    }
}

Unregistering

// Objective-C
[[AppsFlyerLib shared] unregisterSessionReadyListener];
// Swift
AppsFlyerLib.shared().unregisterSessionReadyListener()

Check session readiness

isSessionReady returns YES (true in Swift) when the session-ready listener has fired in the current foreground cycle. Use it to check readiness status from code paths that execute outside the listener callback — for example, deferred logic that runs after the initial startup sequence.

BOOL ready = [[AppsFlyerLib shared] isSessionReady];
let ready = AppsFlyerLib.shared().isSessionReady()

Full example

The following example demonstrates how to initialize and start the SDK without pre-conditions.

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    [[AppsFlyerLib shared] initWithDevKey:@"<YOUR_DEV_KEY>" appleAppId:@"<APPLE_APP_ID>"];

    // Optional - only needed if supporting Universal Links
    [[AppsFlyerLib shared] handleLaunchOptions:launchOptions];

    [[AppsFlyerLib shared] registerSessionReadyListener:^{
        // Collect ATT here if required before start
        [[AppsFlyerLib shared] start];
    }];
    return YES;
}
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    AppsFlyerLib.shared().initialize(devKey: "<YOUR_DEV_KEY>", appId: "<APPLE_APP_ID>")

    // Optional - only needed if supporting Universal Links
    AppsFlyerLib.shared().handleLaunchOptions(launchOptions)

    AppsFlyerLib.shared().registerSessionReadyListener {
        // Collect ATT here if required before start
        AppsFlyerLib.shared().start()
    }
    return true
}

See Setting the Customer User ID to associate a CUID with this integration, and how to configure SKAN / AdAttributionKit postback copies.


Enabling debug mode

Optional

You can enable debug logs by setting isDebug to true:

[AppsFlyerLib shared].isDebug = true;
AppsFlyerLib.shared().isDebug = true
📘

Note

To see full debug logs, make sure to set isDebug before invoking other SDK methods.

🚧

Warning

To avoid leaking sensitive information, make sure debug logs are disabled before distributing the app.

📘

Note

Alternatively, you can enable debug mode at build time by setting debug_mode to true in your AppsFlyerLibConfig.plist file. See Configuring the SDK with AppsFlyerLibConfig.plist above.


Test the integration

Easily test with our SDK wizard

Note

If you prefer not to use our recommended wizard, you can find detailed manual testing instructions here.

For a full troubleshooting checklist, see Troubleshooting.

Creating an iOS debug app

Optional

You can utilize Xcode's compilation configuration capabilities to configure an easy-to-use debug app. It will enable you to switch between your debug and production apps by tapping into Xcode's active compilation conditions.

📘

Note

If you don't mind mixing production data with test traffic, you can skip this section. All tests can be performed for both production and debug apps.

This is achieved by configuring a User-Defined Setting in your project's Build Settings and exposing it via an info.plist property.

Step 1: Add a debug app to AppsFlyer
Add a new pending iOS app to AppsFlyer or ask a team member with dashboard access to add it. Choose any available app ID–You will need it in step 3. Make sure the ID is 9 digits and starts with four 1s, for example, 111167538.

Step 2: Add a User-Defined Setting

  1. In Xcode, in the file navigator view, select your project root and go to Build Settings.
  2. Click + in the toolbar and select Add User-Defined Setting. In this case, we name it AF_APP_ID.
  3. Expand the newly created User-Defined Setting:
    • Set the Debug Conditional Setting to your test app's app ID (mentioned in step 1)
    • Set the Release Conditional Setting to your production app's app ID.

Step 3: Expose app IDs via info.plist
Go to the project's info.plist and add a new property (called AFAppID in this case). Set its value to $(AF_APP_ID) (based on the User-Defined Setting name in step 2).

Step 4: Retrieve and set the app ID
To access and use app ID during SDK initialization, add the following code to didFinishLaunchingWithOptions in your AppDelegate:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // ...
    guard let appID : String = Bundle.main.object(forInfoDictionaryKey: "AFAppID") as? String else {
        fatalError("Cannot find app ID")
    }
    AppsFlyerLib.shared().appleAppID = appID
    // ...
    return true
}

Step 5: Run app using Debug build configuration
To change the active build configuration:

  1. go to Product > Scheme > Edit Scheme....
  2. Select Run and change the Build configuration to Debug or Release, as needed.

Now, when you use the Debug configuration to build your app, Xcode will use the debug app ID that you configured in step 2.


Did this page help you?