appsflyer-react-native-plugin icon indicating copy to clipboard operation
appsflyer-react-native-plugin copied to clipboard

appsFlyer.onDeepLink() Not Triggering in Kill State on iOS with Latest Version

Open Akilramki opened this issue 8 months ago • 26 comments

Summary: After upgrading to React Native 0.79.0 and [email protected], the appsFlyer.onDeepLink() listener stops consistently firing in iOS kill state. It works sometimes, but most of the time it doesn't. The same implementation worked reliably in [email protected] and [email protected].

✅ Previously Working Versions React Native: 0.71.11 AppsFlyer SDK: react-native-appsflyer@^6.12.2 In the working setup, the OneLink consistently triggered appsFlyer.onDeepLink() in: Foreground ✅ Background ✅ Kill State ✅

📄 Universal Link (AASA) Setup Hosted at: https://test.onelink.me/.well-known/apple-app-site-association (sample url) MIME Type: application/pkcs7-mime (.p7m) AASA file verified and working correctly

Objective-C Code That Worked AppDelegate.mm

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
   if ([RCTLinkingManager application:app openURL:url options:options]) return YES;
   [[AppsFlyerAttribution shared] handleOpenUrl:url options:options];
   return YES;
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
  [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
  [[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
  return YES;
}

App.js

useEffect(() => {
  appsFlyer.onDeepLink(res => {
    console.log('onDeepLink:', res);
  });

  appsFlyer.initSdk({
    devKey: 'XXX',
    appId: '123456789',
    isDebug: true,
    onInstallConversionDataListener: true,
    onDeepLinkListener: true,
    timeToWaitForATTUserAuthorization: 10, 
  }, 
  result => console.log('SDK init success', result),
  error => console.log('SDK init error', error));
}, []);

❌ Current Setup (RN 0.79.0 + SDK 6.16.2)

🔧Affected Versions React Native: 0.79.0 AppsFlyer SDK: react-native-appsflyer@^6.16.2 iOS Version: Tested on iOS 17.x

🧪 Current Behavior (v6.16.2 + RN 0.79.0) Foreground: appsFlyer.onDeepLink() ✅ Background: appsFlyer.onDeepLink() ✅ Kill State: appsFlyer.onDeepLink() ❌ (Inconsistent or not firing at all)

📄 Universal Link (AASA) Setup ✅ No changes were made to the AASA configuration between the working and non-working versions. ✅ Same setup used in working version ([email protected], [email protected]) Hosted at: https://test.onelink.me/.well-known/apple-app-site-association (sample url) MIME Type: application/pkcs7-mime (.p7m) AASA file verified and working correctly

Swift AppDelegate Code

AppDelegate.swift

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    AppsFlyerLib.shared().appsFlyerDevKey = "XXX"
    AppsFlyerLib.shared().appleAppID = "123456789"
    AppsFlyerLib.shared().delegate = self
    AppsFlyerLib.shared().isDebug = true
    AppsFlyerLib.shared().start()
    return true
  }

  // iOS 9+ - Open URI Scheme
 func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    AppsFlyerLib.shared().handleOpen(url, options: options) // no return value
    let handledRCT = RCTLinkingManager.application(app, open: url, options: options)
    return handledRCT // return only the actual Bool
}


  // iOS 8 and below - Open URI Scheme
  func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
      AppsFlyerLib.shared().handleOpen(url, sourceApplication: sourceApplication, withAnnotation: annotation)
      return true
  }

  // Universal Links
func application(
  _ application: UIApplication,
  continue userActivity: NSUserActivity,
  restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
    let handledRCT = RCTLinkingManager.application(
        application,
        continue: userActivity,
        restorationHandler: restorationHandler
    )

    AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)

    return handledRCT
}

React Native

App.js

useEffect(() => {
  appsFlyer.onDeepLink(res => {
    console.log('onDeepLink:', res);
  });

  appsFlyer.initSdk({
    devKey: 'XXX',
    appId: '123456789',
    isDebug: true,
    onInstallConversionDataListener: true,
    onDeepLinkListener: true,
    timeToWaitForATTUserAuthorization: 10, 
  }, 
  result => console.log('SDK init success', result),
  error => console.log('SDK init error', error));
}, []);

Akilramki avatar Jun 14 '25 07:06 Akilramki

Hi Team,

Is there any update on this issue?

We’ve observed that when an AppsFlyer Universal Link is clicked while the app is in the background, the appsFlyer.onDeepLink listener is triggered as expected.

However, when the app is launched from a cold (quit) state via the same Universal Link, the appsFlyer.onDeepLink listener does not get called.

harpreet-appinventiv avatar Jun 26 '25 14:06 harpreet-appinventiv

i also face same issue class AppDelegate: RCTAppDelegate { override func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { AppsFlyerLib.shared().handleOpen(url, sourceApplication: sourceApplication, withAnnotation: annotation) return true }

override func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { AppsFlyerLib.shared().handleOpen(url, options: options) RCTLinkingManager.application(application, open: url, options: options) return true }

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

......

daniyalrathore14 avatar Jun 27 '25 06:06 daniyalrathore14

Hi, I have the same problem with cold start on ios, is there any solution?

chizhkov422 avatar Jul 01 '25 09:07 chizhkov422

any success on this issue?

maheshKumarZazmic avatar Jul 03 '25 08:07 maheshKumarZazmic

I faced same issues?? Any solution???

Nayan-Infynno avatar Jul 08 '25 13:07 Nayan-Infynno

any solution for this issue? please help

vungo99 avatar Jul 11 '25 11:07 vungo99

same issue with expo 53

Keshav-1973 avatar Jul 15 '25 06:07 Keshav-1973

I believe the issue is a limitation from AppsFlyer. Please see the link below: https://www.appsflyer.com/use-cases/customer-experience-deep-linking/universal-linking-challenges-ios-10-3/

I encountered this issue when copying and pasting the OneLink into a browser—it doesn't work. Try this instead: generate a QR code from the OneLink and scan it. It will open and trigger onDeeplinking. This issue only occurs on iOS.

And if you wanna fix this issue, please check fallback url ...

vungo99 avatar Jul 15 '25 06:07 vungo99

I found an issue: on iOS, deeplinks/appsFlyer.onDeeplink() do not work when app is in Killed State, in the two cases below:

  • Universal Links don’t load in when pasted into browsers
  • Facebook does not possible to deep link from posts to the Facebook feed

Solution: Create function to handle fallback uri from one link (instead of function callback inside appsFlyer.onDeeplink())

Step 1: Config the fallback link in AppsFlyer Dashboard

Step 2: Use Linking for get URL fallback Code Example below

import { Linking } from 'react-native';

const getFallbackData = async (): Promise<UnifiedDeepLinkData | undefined> => {
     const url = await Linking.getInitialURL();
     if (!url || !url.startsWith('abc')) return;

     const url = new URL(urlString);
     const params: Record<string, string> = {};
     for (const [key, value] of url.searchParams.entries()) {
        params[key] = value;
      }

     return {
           url,
           params
     };
};

Step 3: Create new function handle fallback like callback in appsFlyer.onDeeplink(callback)

Tuan2109 avatar Jul 15 '25 15:07 Tuan2109

I found an issue: on iOS, deeplinks/appsFlyer.onDeeplink() do not work when app is in Killed State, in the two cases below:

  • Universal Links don’t load in when pasted into browsers
  • Facebook does not possible to deep link from posts to the Facebook feed

Solution: Create function to handle fallback uri from one link (instead of function callback inside appsFlyer.onDeeplink())

Step 1: Config the fallback link in AppsFlyer Dashboard

Step 2: Use Linking for get URL fallback Code Example below

import { Linking } from 'react-native';

const getFallbackData = async (): Promise<UnifiedDeepLinkData | undefined> => {
const url = await Linking.getInitialURL();
if (!url || !url.startsWith('abc')) return;

const query = url.split('?')[1];
if (!query) return;

const params = Object.fromEntries(
query.split('&').map((pair) => pair.split('=').map(decodeURIComponent)),
);

return {
deepLinkStatus: 'FOUND',
status: 'success',
type: 'onDeepLinking',
data: params,
isDeferred: false,
};
};

Step 3: Create new function handle fallback like callback in appsFlyer.onDeeplink(callback)

kaka this work for me. tks a lot 👍

vungo99 avatar Jul 16 '25 02:07 vungo99

@Tuan2109 what if I am using short url and doesn't have parameters attached to it and can only be fetched using deeplink function then how we can achieve it ? as it seems that parameters are not fetched from this way. for (const [key, value] of url.searchParams.entries()) { params[key] = value; } and using alternative way like this :

const params = Object.fromEntries( query.split('&').map((pair) => pair.split('=').map(decodeURIComponent)), ); will require parameters to be attached to the uri means long uri required.

maheshKumarZazmic avatar Jul 16 '25 07:07 maheshKumarZazmic

Look at the difference between Objective-C and Swift code. For Objective-C, you are using (as written in our documentation) AppsFlyerAttribution, while AppsFlyerLib is used in your Swift example. The issue with AppsFlyerLib is that the deep link resolution is done before the bridge between JS and Native layers is established. To handle such cases, you should use AppsFlyerAttribution, which waits for the bridge to be ready before performing the deep link resolution. To work with AppsFlyerAttribution in Swift, you'll have to create a Bridging Header file.

For any future assistant, please open a support ticket.

pazlavi avatar Jul 16 '25 13:07 pazlavi

👋 Hi @Akilramki and Thank you for reaching out to us. You can contact AppsFlyer support through the Customer Assistant Chatbot for assistance with troubleshooting issues or product guidance. To do so, please follow this article.

github-actions[bot] avatar Jul 16 '25 13:07 github-actions[bot]

Thanks a lot for this comment @pazlavi — it helped me identify and solve the exact issue I was facing with deep links not firing on cold start in iOS when using React Native.

For anyone integrating react-native-appsflyer in a Swift-based project, here's what finally worked for me:

✅ Problem: Using AppsFlyerLib.shared() in AppDelegate.swift was causing deep links (especially OneLink) to fail on cold start — exactly as mentioned here: the resolution was happening before the JS bridge was ready.

✅ Fix: Replaced AppsFlyerLib with AppsFlyerAttribution in Swift, like so:

func application( _ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:] ) -> Bool { AppsFlyerAttribution.shared().handleOpen(url, options: options)

// React Native Linking
return RCTLinkingManager.application(application, open: url, options: options)

}

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

Did not try to import AppsFlyerAttribution in Swift directly (which fails)

➡️ Instead, added this to Bridging Header file in your project:

#import <RNAppsFlyer.h>

This exposes the internal Objective-C implementation from react-native-appsflyer, including AppsFlyerAttribution.

✅ Result: After making this change, onDeepLink() is reliably triggered even on cold start.

chizhkov422 avatar Jul 23 '25 12:07 chizhkov422

Looks like this has been fixed, both in the docs as well as the Expo plugin. Can we close this?

lennartschoch avatar Aug 19 '25 14:08 lennartschoch

Thanks a lot for this comment @pazlavi — it helped me identify and solve the exact issue I was facing with deep links not firing on cold start in iOS when using React Native.

For anyone integrating react-native-appsflyer in a Swift-based project, here's what finally worked for me:

✅ Problem: Using AppsFlyerLib.shared() in AppDelegate.swift was causing deep links (especially OneLink) to fail on cold start — exactly as mentioned here: the resolution was happening before the JS bridge was ready.

✅ Fix: Replaced AppsFlyerLib with AppsFlyerAttribution in Swift, like so:

func application( _ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:] ) -> Bool { AppsFlyerAttribution.shared().handleOpen(url, options: options)

// React Native Linking
return RCTLinkingManager.application(application, open: url, options: options)

}

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

Did not try to import AppsFlyerAttribution in Swift directly (which fails)

➡️ Instead, added this to Bridging Header file in your project:

#import <RNAppsFlyer.h>

This exposes the internal Objective-C implementation from react-native-appsflyer, including AppsFlyerAttribution.

✅ Result: After making this change, onDeepLink() is reliably triggered even on cold start.

This works

amitkumarcoding avatar Aug 21 '25 04:08 amitkumarcoding

Hey Guys, it does not work for me in after adding in the bridging header.. @lennartschoch Where do you think it is fixed ? can you share a link ? Can someone share full swift example

singhagam1 avatar Aug 21 '25 17:08 singhagam1

Same issue on react-native project, after update to React Native 0.79.0 and [email protected]

MarySnopok avatar Aug 28 '25 14:08 MarySnopok

@singhagam1 @MarySnopok

Please add the following changes for AppsFlyer integration using Swift langugae

  1. Update the bridge header #import <RNAppsFlyer.h>

  2. Modify AppDelegate.swift

Add the required import:

import AppsFlyerLib

Then include the following methods:

// MARK: - AppsFlyer Deeplink Handling

// iOS 9 and above: Handle URI-scheme deep links
func application(
  _ application: UIApplication,
  open url: URL,
  options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
  AppsFlyerLib.shared().handleOpen(url, options: options)
  return true
}

// iOS 8 and below: Handle URI-scheme deep links
func application(
  _ application: UIApplication,
  open url: URL,
  sourceApplication: String?,
  annotation: Any
) -> Bool {
  AppsFlyerLib.shared().handleOpen(url, sourceApplication: sourceApplication, withAnnotation: annotation)
  return true
}

// Universal Links handling
func application(
  _ application: UIApplication,
  continue userActivity: NSUserActivity,
  restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
  AppsFlyerLib.shared().continue(userActivity, restorationHandler: { (array: [Any]?) in
    restorationHandler(array as? [UIUserActivityRestoring])
  })
  return true
}


amitkumarcoding avatar Aug 28 '25 15:08 amitkumarcoding

@amitkumarcoding Thank you for sharing but we have those configs at place as those are part of the initial integration manual and they worked well before the upgrade. The issue is exactly how it is specified in the ticket description that performance is inconsistent and it fires 1 in 3 from kill (cold) state.

MarySnopok avatar Aug 29 '25 12:08 MarySnopok

The solution was found in the sample code.

Bridge header is not required.

https://github.com/AppsFlyerSDK/appsflyer-onelink-ios-sample-apps/blob/a96399329a369b30263ea4f8cc4558029ea603b3/swift/basic_app/basic_app/AppDelegate.swift

// package.json

"react-native": "0.81.1",
"react-native-appsflyer": "^6.17.5",
// AppDelegate.swift

import UIKit
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider
import AppsFlyerLib

@main
class AppDelegate: UIResponder, UIApplicationDelegate, AppsFlyerLibDelegate, DeepLinkDelegate {
  var window: UIWindow?

  var reactNativeDelegate: ReactNativeDelegate?
  var reactNativeFactory: RCTReactNativeFactory?

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    let delegate = ReactNativeDelegate()
    let factory = RCTReactNativeFactory(delegate: delegate)
    delegate.dependencyProvider = RCTAppDependencyProvider()

    reactNativeDelegate = delegate
    reactNativeFactory = factory

    window = UIWindow(frame: UIScreen.main.bounds)

    factory.startReactNative(
      withModuleName: "my_app",
      in: window,
      launchOptions: launchOptions
    )

    return true
  }
  
  // MARK: - AppsFlyerLibDelegate
  func onConversionDataSuccess(_ conversionInfo: [AnyHashable : Any]) {
    // Handle conversion data success
  }
  
  func onConversionDataFail(_ error: Error) {
    // Handle conversion data failure
  }
  
  // MARK: - DeepLinkDelegate
  func didResolveDeepLink(_ result: DeepLinkResult) {
    // Handle deep link
  }
  
  // MARK: - URL Handling
  func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil)
    return true
  }
  
  // Open URI-scheme for iOS 8 and below
  func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
    AppsFlyerLib.shared().handleOpen(url, sourceApplication: sourceApplication, withAnnotation: annotation)
    return true
  }
  
  // Open URI-scheme for iOS 9 and above
  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    AppsFlyerLib.shared().handleOpen(url, options: options)
    return true
  }
}

class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
  override func sourceURL(for bridge: RCTBridge) -> URL? {
    self.bundleURL()
  }

  override func bundleURL() -> URL? {
#if DEBUG
    RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
    Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
  }
}
// util_appsflyer.ts

import { Platform } from 'react-native';
import appsFlyer from 'react-native-appsflyer';

appsFlyer.initSdk(
  {
    devKey: "123456",
    isDebug: true,
    appId: "123456",
    onInstallConversionDataListener: false,
    onDeepLinkListener: true, //Optional
    timeToWaitForATTUserAuthorization: 10, //for iOS 14.5
  },
  () => null,
  () => null,
);
// index.js

/**
 * @format
 */
import { AppRegistry } from 'react-native';

import { name as appName } from './app.json';
import App from './src/App';
import './src/shared/utils/util_appsflyer';

AppRegistry.registerComponent(appName, () => App);
// useDeepLink.ts

const useDeepLink = () => {
  useEffect(() => {
    appsFlyer.onDeepLink(result => {
      console.log('result', result);
    });

    return (): void => {
      appsFlyer.stop(true);
    };
  }, []);
};

export default useDeepLink;
useDeepLink();

Image

qnrjs42 avatar Sep 25 '25 01:09 qnrjs42

pazlavi

To handle such cases, you should use AppsFlyerAttribution, which waits for the bridge to be ready before performing the deep link resolution.

Thank you for the suggestion, it will make things easier for us from now on!

We've been dealing with this issue since upgrading to React Native 0.77, which is the first to have swift based AppDelegate. We've even been in touch with product support with no luck.

Can it be true that it's not mentioned anywhere in the documentation, that you need to do this trick, to support deep links on RN >= 0.77? Seems link new updates to the RN SDK has been released continously in the meantime since RN 77 came out, with no instructions of this kind.

esben-sg avatar Sep 26 '25 12:09 esben-sg

@pazlavi @chizhkov422 many thanks! Wish I can buy you a beer/tee/coffee/${drinkOfYourChoice}

For folks who are rusty on xcode/ios here is an instruction how to create the bridging header

XCode 26.0.1:

  1. Files>New>File from template>Header
  2. File Name AppDelegate-Bridging-Header.h file content #import <RNAppsFlyer.h>
  3. Build Setting > Swift Compiler - General > Objective-C Bridging Header
  4. Debug/Release: app/AppDelegate-Bridging-Header.h
  5. Build app
Image

ahtokca avatar Oct 16 '25 07:10 ahtokca

same issue

bodrius avatar Oct 16 '25 12:10 bodrius

Is anyone using a fix for a expo managed workflow? (using prebuild)

arapocket avatar Oct 27 '25 20:10 arapocket

I'm having the same problem.

RN 0.77.3 appsflyer 6.17.7

Using AppDelegate.mm instead of Swift.

lautaroml avatar Dec 11 '25 15:12 lautaroml

So no expo users?

arapocket avatar Jan 05 '26 06:01 arapocket

Thanks a lot for this comment @pazlavi — it helped me identify and solve the exact issue I was facing with deep links not firing on cold start in iOS when using React Native.

For anyone integrating react-native-appsflyer in a Swift-based project, here's what finally worked for me:

✅ Problem: Using AppsFlyerLib.shared() in AppDelegate.swift was causing deep links (especially OneLink) to fail on cold start — exactly as mentioned here: the resolution was happening before the JS bridge was ready.

✅ Fix: Replaced AppsFlyerLib with AppsFlyerAttribution in Swift, like so:

func application( _ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:] ) -> Bool { AppsFlyerAttribution.shared().handleOpen(url, options: options)

// React Native Linking
return RCTLinkingManager.application(application, open: url, options: options)

}

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

Did not try to import AppsFlyerAttribution in Swift directly (which fails)

➡️ Instead, added this to Bridging Header file in your project:

#import <RNAppsFlyer.h>

This exposes the internal Objective-C implementation from react-native-appsflyer, including AppsFlyerAttribution.

✅ Result: After making this change, onDeepLink() is reliably triggered even on cold start.

Thanks @ahtokca, @pazlavi & @chizhkov422!

This solved the issue for me. I used; "react-native-appsflyer": "~6.17.8" "react-native": "0.81.4",

You have to create bridging header file via Xcode. Below are the steps;

  • File > + New > File from Template...
  • Name your file with AppDelegate-Header.h for example
  • Fill the content with this one line "#import <RNAppsFlyer.h>"
  • Open build setting > All Tab
  • Find "Swift Compiler - General"
  • Expand Objective-C Bridging Header
  • Click the plus sign of each of your Build Configuration
  • Fill the value with "$(SRCROOT)/AppDelegate-Header.h"
  • Change the AppDelegate.swift file, remove "import AppsFlyerLib"
  • Change "AppsFlyerLib.shared()" with "AppsFlyerAttribution.shared()"
  • AppsFlyerAttribution will be retrieved from your AppDelegate-Header.h file, so no need to "import AppsFlyerAttribution"
  • Build the App

ltujiba avatar Jan 13 '26 17:01 ltujiba