Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Deep link handlers not called when app is fully closed #223

Open
lucasroca opened this issue Dec 14, 2020 · 80 comments
Open

[iOS] Deep link handlers not called when app is fully closed #223

lucasroca opened this issue Dec 14, 2020 · 80 comments

Comments

@lucasroca
Copy link

lucasroca commented Dec 14, 2020

Plugin Version

6.1.20

On what Platform are you having the issue?

iOS

What did you do?

Installed the latest version of the plugin

What did you expect to happen?

onDeepLink to be called

What happened instead?

The handler wasn't triggered

Please provide any other relevant information.

On iOS, react-native-appsflyer version 6.1.10, when clicking a OneLink the app opens butonAppOpenAttribution is not triggered when the app is fully closed.

Then I upgraded to react-native-appsflyer version 6.1.20 to test unified links. Again, onDeepLink works on every case except when the app is installed but fully closed and I open a OneLink.

I tried both on simulator and real device with no luck.

@goldyukol
Copy link

Also problem, please fix this bag

@Voidozzer
Copy link

Same problem here

Plugin 6.1.20
React Native 0.63.3

Appears only on iOS, on Android works as expected.

@lucasroca
Copy link
Author

Hi @amit-kremer93, do you have any updates on this?

@amit-kremer93
Copy link
Contributor

amit-kremer93 commented Dec 20, 2020

Did you follow this??
For now, you can use onInstallConversionData listener to handle direct deep link in iOS by checking if
is_first_launch === false. it's not the best solution but it will work until we figure it out

@rborn
Copy link

rborn commented Dec 22, 2020

onInstallConversionData is not enough to get all the params.
I had to downgrade to 6.0.50 to restore the behaviour :(

@goldyukol
Copy link

goldyukol commented Dec 29, 2020

onInstallConversionData is not enough to get all the params.
I had to downgrade to 6.0.50 to restore the behaviour :(

6.0.50 - does not handled when the app is turned on :(
latest version - handled when app on, but not handled when start :(

can developers combine these 2 functions?

or maybe add in onInstallConversionData all params?

@amit-kremer93
Copy link
Contributor

amit-kremer93 commented Dec 31, 2020

@goldyukol in which type of deep link do you use? URL scheme or universal link?
6.0.50 works fine for me when the app in the background and when fully closed

@rborn
Copy link

rborn commented Jan 5, 2021

@amit-kremer93 how does it work for you ? we use appsflyer links

@goldyukol
Copy link

@goldyukol in which type of deep link do you use? URL scheme or universal link?
6.0.50 works fine for me when the app in the background and when fully closed

In latest version this features doesn't work

@lucasroca
Copy link
Author

lucasroca commented Jan 8, 2021

Did you follow this??
For now, you can use onInstallConversionData listener to handle direct deep link in iOS by checking if
is_first_launch === false. it's not the best solution but it will work until we figure it out

Yes, I've followed those steps. The issue still remains. As mentioned by @rborn, using onInstallConversionData doesn't help much.

@goldyukol in which type of deep link do you use? URL scheme or universal link?
6.0.50 works fine for me when the app in the background and when fully closed

I'm using universal links.

@csantarin
Copy link

@amit-kremer93 like the rest of us here, definitely still happening on my end.

On relaunching the app, onAppOpenAttribution doesn't happen, but if it was running in the background, direct deep links would successfully trigger it.

@amit-kremer93
Copy link
Contributor

It seems like the issue started after the native SDK was upgraded and we are working on a solution.
Sorry for the inconvenience.

@amit-kremer93
Copy link
Contributor

I released a new version (6.1.40) with a fix for the onAppOpenAttribution in iOS. The implementation has slightly changed so please see this but the flow is the same. Please let us know here if there is any problem. thanks!

@csantarin
Copy link

csantarin commented Jan 18, 2021

Update

Those who are running into this might find #223 (comment) useful.

Reasoning here and here.


Please let us know here if there is any problem.

@amit-kremer93 Yup, unfortunately, there is one.

I opened the app from the phone's email client. Then shortly after, the app crashed before it could even start logging anything to the JavaScript console. Here's the native log. ML.Debug refers to my local app process.

default	12:17:28.772654+0800	ML.Debug	[DEBUG] AppsFlyer: [HTTP] 
Result: {
    data = {length = 4, bytes = 0x226f6b22};
    dataStr = "\"ok\"";
    retries = 2;
    statusCode = 200;
    taskIdentifier = 3;
}                
Error: (null)
default	12:17:28.772732+0800	ML.Debug	[DEBUG] AppsFlyer: Loading conversion data
default	12:17:28.773572+0800	ML.Debug	[DEBUG] AppsFlyer: [GCD-A02] -[RNAppsFlyer onConversionDataSuccess:]:
{
    "af_message" = "organic install";
    "af_status" = Organic;
    "install_time" = "2021-01-18 04:06:20.689";
    "is_first_launch" = 0;
}
default	12:17:28.775784+0800	ML.Debug	[DEBUG] AppsFlyer: [CACHE] Deleting file: /var/mobile/Containers/Data/Application/1E631D5D-803D-4A15-8B56-FA65694DFF2D/Library/Caches/appsflyer-v1/632636244.2472.plist
default	12:17:28.775850+0800	ML.Debug	[DEBUG] AppsFlyer: [HTTP] Dealloc. Time elapsed for: `160-1610943444.211549` - 3.944231986999512
default	12:18:09.942508+0800	ML.Debug	*** Assertion failure in -[RCTEventEmitter sendEventWithName:body:](), /Users/csantarin/moneylion/mobile-app/node_modules/react-native/React/Modules/RCTEventEmitter.m:50
default	12:18:10.253181+0800	ML.Debug	*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error when sending event: onAttributionFailure with body: {"status":"failure","type":"onAttributionFailure","data":"Authentication Failed"}. Bridge is not set. This is probably because you've explicitly synthesized the bridge in RNAppsFlyer, even though it's inherited from RCTEventEmitter.'
*** First throw call stack:
(0x1853619d8 0x1996cab54 0x18527050c 0x1865b9238 0x1051ecf80 0x1058948a8 0x1058946c8 0x186657614 0x1852e1bf0 0x1852e1af0 0x1852e0e38 0x1852db3e0 0x1852daba0 0x19c018598 0x187bca3d8 0x187bcf958 0x104604564 0x184fb9568)

@amit-kremer93
Copy link
Contributor

@csantarin {"status":"failure","type":"onAttributionFailure","data":"Authentication Failed"} means that you have an issue with your devKey. about the crash, i released an hot fix.

@haipham-kontist
Copy link

haipham-kontist commented Jan 18, 2021

@amit-kremer93 , I'm testing version 6.1.41

  • On android: it works in either scenarios, when app is in background, or when it's fully closed.
  • On ios: it doesn't work if app is fully closed. It works if app is in background.

I already updated didFinishLaunchingWithOptions.

@goldyukol
Copy link

@ amit-kremer93 , тестирую версию6.1.41

  • На Android: он работает в любом сценарии, когда приложение находится в фоновом режиме или когда оно полностью закрыто.
  • На ios: не работает, если приложение полностью закрыто. Работает, если приложение находится в фоновом режиме.

Я уже обновился didFinishLaunchingWithOptions.

How you updated didFinishLaunchingWithOptions?

Send code pls

@haipham-kontist
Copy link

How you updated didFinishLaunchingWithOptions?

Send code pls

You can find it in the updated guide: https://github.com/AppsFlyerSDK/appsflyer-react-native-plugin/blob/master/Docs/Guides.md#import

  if(_AppsFlyerdelegate == nil){
    _AppsFlyerdelegate = [[RNAppsFlyer alloc] init];
  }
  [[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];

@goldyukol
Copy link

@amit-kremer93 , I'm testing version 6.1.41

  • On android: it works in either scenarios, when app is in background, or when it's fully closed.
  • On ios: it doesn't work if app is fully closed. It works if app is in background.

I already updated didFinishLaunchingWithOptions.

  [[FBSDKApplicationDelegate sharedInstance] application:application
    didFinishLaunchingWithOptions:launchOptions];

added after?

  if(_AppsFlyerdelegate == nil){
    _AppsFlyerdelegate = [[RNAppsFlyer alloc] init];
  }
  [[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];

@goldyukol
Copy link

goldyukol commented Jan 18, 2021

I released a new version (6.1.40) with a fix for the onAppOpenAttribution in iOS. The implementation has slightly changed so please see this but the flow is the same. Please let us know here if there is any problem. thanks!

Problem is not resolved

@csantarin
Copy link

csantarin commented Jan 18, 2021

Update

Those who are running into this might find #223 (comment) useful.

Reasoning here and here.


@goldyukol not-maintainer here. On 6.1.41, I imagine it should look like this on a brand new AppDelegate.m:

#import <RNAppsFlyer.h>
#import <React/RCTLinkingManager.h>
#import <AppsFlyerLib/AppsFlyerLib.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // Flipper init here...

  if(_AppsFlyerdelegate == nil){
    _AppsFlyerdelegate = [[RNAppsFlyer alloc] init];
  }
  [[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];

  // React Native Bridge and Root View init here...
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] 
    initWithBridge:bridge
    moduleName:@"FiftyViewsUnderTheAppiumSea"
    initialProperties:nil
  ];

  // More on React Native Bridge and Root View init here...

  return YES;
}

Correct me if I'm wrong, @amit-kremer93. That's how I'm initializing the SDK on my end.

@csantarin
Copy link

{"status":"failure","type":"onAttributionFailure","data":"Authentication Failed"} means that you have an issue with your devKey. about the crash, i released an hot fix.

Right you are, @amit-kremer93. But I've followed the iOS SDK / RN plugin integration guide. The devKey and appId are correct. So, I don't get it. Why would I have this issue?

On my end, .initSdk(config) is called right after subscribers, i.e. onInstallConversionData, onAppOpenAttribution, etc.

@jwebcat
Copy link

jwebcat commented Jan 19, 2021

@amit-kremer93 we are also experiencing a similar issue. On the latest version of the plugin (6.1.41) for iOS: if the app is fully closed, after tapping a onelink in the phones email client, the phone crashes with the following error

[DEBUG] AppsFlyer:                        
[Shortlink] OneLink:(null)                       
[Shortlink] Response Info:{
    data = {length = 21, bytes = 0x41757468656e7469636174696f6e204661696c6564};
    dataStr = "Authentication Failed";
    retries = 0;
    statusCode = 401;
    taskIdentifier = 1;
}                       
[Shortlink] Error: Error Domain=com.appsflyer.sdk.serialize Code=61 "Authentication Failed" UserInfo={NSLocalizedDescription=Authentication Failed}

Everything is setup according to the docs and was not crashing before updating the plugin (although onDeepLink was not firing on iOS). I have made the recommended changes from the new readme to the AppDelegate.m as well.

If the app is in the background, it works as expected i.e. the onDeepLink handler is called and no crash.

@colaquecez
Copy link

Same here, if i open the app and then i use deep link it works, but when the app is completely closed does not work :(

@maballe-melli
Copy link

hi guys, having issue with onDeepLink not getting called when in background. In my case, the onDeepLink is triggered if the app is fully closed. The issue only is when its in background. onInstallConversion does get called even when in background, but i cant get enough params.

Im running appsflyer 6.3.20. Testing in physical device running iOS 14.0.1. I have added domain association, my AppDelegate.m looks like this:

#import "AppDelegate.h"

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "RNSplashScreen.h"
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <CodePush/CodePush.h>
#import <Firebase.h>
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
#import <React/RCTLinkingManager.h>
#import <RNAppsFlyer.h>
#import <AppsFlyerLib/AppsFlyerLib.h>

#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>

static void InitializeFlipper(UIApplication *application) {
  FlipperClient *client = [FlipperClient sharedClient];
  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
  [client addPlugin:[FlipperKitReactPlugin new]];
  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
  [client start];
}
#endif

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{  
  if ([FIRApp defaultApp] == nil) {
    [FIRApp configure];
  }
  
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;
  
#ifdef FB_SONARKIT_ENABLED
  InitializeFlipper(application);
#endif

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"melli"
                                            initialProperties:nil];

  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  
  [RNSplashScreen show];

  [[FBSDKApplicationDelegate sharedInstance] application:application
                           didFinishLaunchingWithOptions:launchOptions];
  
  return YES;
}

//local push notification
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
  
  [[FBSDKApplicationDelegate sharedInstance] application:application
                                                 openURL:url
                                                 options:options];
  
  [RCTLinkingManager application:application openURL:url options:options];
  
  return YES;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [CodePush bundleURL];
#endif
}

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

  return YES;
}

@end

@maballe-melli
Copy link

aaaa

I have also tried following the code snippet suggested but im getting this error in xcode

@AlphaGit
Copy link

AlphaGit commented Sep 3, 2021

@maballe-melli In case you haven't been able to solve this yet (or for any other future visitors), that property needs to be declared so it can be used. That is done in the header file, so go to the AppDelegate.h file and inside the interface (between @interface and @end) add the following line:

@property (nonatomic) RNAppsFlyer *AppsFlyerdelegate;

Here's how my AppDelegate.h file looks after this patch (notice that in this case I used an uppercase D on Delegate, you'll need to adjust to your own case):

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic) RNAppsFlyer *AppsFlyerDelegate;

@end

@digosantos
Copy link

Hi everyone!

I've been struggling with the same issue for some weeks while working with Flutter as you can see here.

The behavior is the same as @haipham-kontist mentioned:

  • On android: it works in either scenarios, when app is in background, or when it's fully closed.
  • On iOS: it doesn't work if app is fully closed. It works if app is in background.

I'm leaving this comment here just to warn you about this and try to help you isolate the problem. I think that the root cause is that there might be something happening with application open url method not being triggered by the time the app is closed.

I've already read a lot suggestions pointing to scenes in iOS but turns out that my project is only using AppDelegate and I already removed everything scene-related.

Also, I created a side native project to test and it works just fine in all scenarios. The same behavior happens while opening the app through URL schemes.

So I think this issue is not AppsFlyer related only. I believe those points reinforces the idea that we should probably focus our efforts on investigating the hybrid integration among open url method.

Does anybody knows if we should rely on any other method to make it work?

Thanks and hope this comment can help somebody else.

@jacprada
Copy link

jacprada commented Oct 6, 2021

@digocse thank you for the insight!

I am currently having the same iOS specific issue while trying to integrate a different react native library for push notifications and deep links. Your point on this not being an AppsFlyer related issue only finds confirmation from my side!

Sadly I do not have any tips to share on how to address this. 😢

@kamalpandey
Copy link

kamalpandey commented Jun 25, 2022

Having similar issue with 6.5.21

Any solution here?

@murilokrugner
Copy link

I'm having the same problem, still haven't found the solution?

@amit-kremer93
Copy link
Contributor

Use latest version, clean your project and try again

@OrLevy23
Copy link

OrLevy23 commented Nov 2, 2022

Issue happens to me when trying to click on deep link from a closed app.
looked at the native code and saw this error:
AppsFlyer Debug: Error when sending event: onDeepLinking with body: {"status":"success","deepLinkStatus":"FOUND","data":{"show_screen":"edit_profile","campaign":"a","deep_link_sub1":"extra_values","af_dp":"tf1:\/\/edit-profile-picture","deep_link_value":"update_profile_picture","media_source":"User_invite"},"type":"onDeepLinking","isDeferred":false}. RCTCallableJSModules is not set. This is probably because you've explicitly synthesized the RCTCallableJSModules in RNAppsFlyer, even though it's inherited from RCTEventEmitter.

edit: this happens when I try to use:
[[AppsFlyerLib shared] setDeepLinkDelegate:_AppsFlyerDelegate];
instead of
[[AppsFlyerLib shared] setDelegate:_AppsFlyerDelegate];

either way does not seem to work for me (updated to latest version but to no avail

@ys-sherzad
Copy link

ys-sherzad commented Dec 21, 2022

@amit-kremer93
@OrLevy23 Hi there, did you find any solution. I'm facing the same problem on iOS, onDeepLink doesn't trigger when app is completely closed, but fires when app is in background.

#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
#import "AppDelegate.h"
#import <CodePush/CodePush.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTRootView.h>
#import <ReactNativeNavigation/ReactNativeNavigation.h>
#import <FBSDKCoreKit/FBSDKCoreKit-swift.h>
#import <Firebase.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <React/RCTLinkingManager.h>
#import <ReactNativeNavigation/RNNEventEmitter.h>
#import "Orientation.h"
#import <AppCenterReactNative.h>
#import <AppCenterReactNativeAnalytics.h>
#import <AppCenterReactNativeCrashes.h>
#import <RNAppsFlyer.h>

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  if ([FIRApp defaultApp] == nil) {
     [FIRApp configure];
   }

  self.window.backgroundColor = [UIColor whiteColor];
  [ReactNativeNavigation bootstrapWithDelegate:self launchOptions:launchOptions];
    
  [self allowPlayingAudioWhenPhoneIsMuted];
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;

  [AppCenterReactNative register];
  [AppCenterReactNativeAnalytics registerWithInitiallyEnabled:true];
  [AppCenterReactNativeCrashes registerWithAutomaticProcessing];

    return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  #if DEBUG
    return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
  #else
    return [CodePush bundleURL];
  #endif
}

- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge {
  return [ReactNativeNavigation extraModulesForBridge:bridge];
}

- (void)allowPlayingAudioWhenPhoneIsMuted {
    NSError *setCategoryErr = nil;
    NSError *activationErr = nil;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&setCategoryErr];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
    [[AVAudioSession sharedInstance] setActive:YES error:&activationErr];
}

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
  return [Orientation getOrientation];
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            sourceApplication:(NSString*)sourceApplication
            annotation:(id)annotation
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {

  [
    [AppsFlyerAttribution shared]
    handleOpenUrl:url
    sourceApplication:sourceApplication
    annotation:annotation
  ];

  [
    [FBSDKApplicationDelegate sharedInstance]
    application:application
    openURL:url
    sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
    annotation:options[UIApplicationOpenURLOptionsAnnotationKey]
  ];

  [RCTLinkingManager
    application:application
    openURL:url
    sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
    annotation:options[UIApplicationOpenURLOptionsAnnotationKey]
  ];

  return YES;
}

- (BOOL)application:(UIApplication *)application willContinueUserActivityWithType:(nonnull NSString *)userActivityType {
  if ([userActivityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
    return YES; // NOTE: using the xcode debbugger so I can see it reaches this state
  }
  return NO;
}

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

  [
    [AppsFlyerAttribution shared]
    continueUserActivity:userActivity
    restorationHandler:restorationHandler];

  return YES;
}

// Reports app open from deep link from apps which do not support Universal Links (Twitter) and for iOS8 and below
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation {
    // version >= 6.2.10
    [[AppsFlyerAttribution shared] handleOpenUrl:url sourceApplication:sourceApplication annotation:annotation];
    return YES;
}

// Reports app open from URL Scheme deep link for iOS 10
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *) options {
    // version >= 6.2.10
    [[AppsFlyerAttribution shared] handleOpenUrl:url options:options];

    return YES;
}

// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
 [RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
 [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
 [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// IOS 10+ Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
  completionHandler();
}
// IOS 4-10 Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
 [RNCPushNotificationIOS didReceiveLocalNotification:notification];
}

-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

@end

"react-native-appsflyer": "^6.9.2"
"react-native": "0.70.6"

@OrLevy23
Copy link

So yes actually it appeared that this does not work in debug mode but only in release

@ys-sherzad
Copy link

@OrLevy23 Appsflyer doesn't work on debug mode, only for published apps. would you mind sharing your AppDelegate.mm? I have tried many things but no luck

@ys-sherzad
Copy link

ys-sherzad commented Dec 21, 2022

Ok, it seems that this line was causing the issue

in didFinishLaunchingWithOptions

return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];

rewriting the above to the following seem to have fixed the problem for me

[[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];

return YES;

@OrLevy23
Copy link

So we have just noticed that this issue reconstructs for us. not sure whether this is a ios version specific issue (happens from ios 16 and above. currently struggling to test on older versions) or build issue.
Went back to the commit that I know for sure that it worked on but I can't seem to make the universal links to work from cold start.

@annasychugina
Copy link

annasychugina commented Jan 25, 2023

Any ideas how to fix it ?

I reproduced it on IOS v16 with UDL.

After clicking on the link the application opens but the internal deeplink on current screen in the app does not work. It happened when app is fully closed

I've already tried this solution

  if(_AppsFlyerdelegate == nil){
    _AppsFlyerdelegate = [[RNAppsFlyer alloc] init];
  }
  [[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];

But it didn't help. The same behavior. Nothing changed.

I use the latest version of react-native-appsflyer

  "react-native-appsflyer": "^6.9.4",
  "react-native": "0.65.3",

@OrLevy23
Copy link

@annasychugina is this something that used to work and stopped for you on iOS 16?

@annasychugina
Copy link

@OrLevy23 no, I didn't check it before. We started implementing UDL links (onelink) and I use onDeepLink method in the latest version of react-native-appsflyer. I didn't check previous versions . So, it doesn't work with cold boot (app opens but there is no redirect to current screen, onDeepLink isn't triggered) In the background everything works correctly.

Any solutions ? I tried everything which was mentioned here. So, think about not using onDeepLink method and start using Linking API (https://reactnative.dev/docs/linking)

But if you have any ideas and hacks how to fix (maybe it worked in previous version ?? because it's very strange, it's critical bug and yet there is no solution ).

It doesn't work in IOS 14.0 and IOS >= 16.0 versions (I didn't check previous)

@annasychugina
Copy link

I also sent email to support.appsflyer.com but no answer

@trqvinh90
Copy link

Hi guys, are there any updates on this issue?

@paul-hart
Copy link

paul-hart commented Apr 13, 2023

I am working on a former hybrid RN app that still had aspects of the code in Swift. Recently, while migrating Segment code, I broke AppsFlyer deeplinking and I was faced with the cold boot issue describe above. We had been using the Segment pod to import the dependency and now it's coming from the node_modules folder. The issue is a race condition between the iOS did launch handler and the RN bridge being loaded.

When the SDK was fully native it didn't care about a bridge! So calling the following would lead to the deeplink handler being called:

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

The first fix was to change it to:

AppsFlyerAttribution.shared().continue(userActivity)

This was ultimately the change the fixed it for me. However because we have more than one deeplinking solution in our app, I need it to return a boolean so that I know it's a valid AppsFlyer link.

If you look at their code, it checks if the bride is loaded.

- (void)continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^_Nullable)(NSArray * _Nullable))restorationHandler{
    if([self RNAFBridgeReady] == YES){
        [[AppsFlyerLib shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
    }else{
        [self setUserActivity:userActivity];
    }
}

So if you are like me and need conditions around returning true, the following solution will work:

if AppsFlyerLib.shared().continue(userActivity, restorationHandler: nil) {
  if !AppsFlyerAttribution.shared().rnafBridgeReady {
    AppsFlyerAttribution.shared().continue(userActivity)
  }
      
  return true
}

return RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)

What happens is the "AppsFlyerLib.shared().continue" function returns a boolean saying that the link applies to AppsFlyer. But because the bridge is not ready it does not get handled. Knowing the bridge is not ready using their boolean, calling "AppsFlyerAttribution.shared().continue" invokes "[self setUserActivity:userActivity]" which solves the race condition.

When the bridge is ready, the AppsFlyerLib works fine.

If the function "setUserActivity" was exposed you could just call that directly, but knowing how it's currently implemented you can assume how it will behave when the bridge is not ready. We have our own boolean that tells us if the bridge is ready, but I chose to use the same one they do so I know 100% it will hit the "setUserActivity" function and not handle the deeplink twice or something.

Honestly I find it very confusing there is both AppsFlyerLib and AppsFlyerAttribution. Also very annoying that the AppsFlyerAttribution version does not provide a boolean.

In psudo code it's like this

if "can open AppsFlyer link, and will trigger the deeplink handler if app was backgrounded" {
  if "RN bridge is not ready" {
    "call function that would handle the link in a backgrounded situation but also saves the activity for after the bridge is loaded"     
  }
  
  "return true because in either case we know we have an AppsFlyer link"
}

Hope this helps someone, this was very frustrating for me. Having two very similar instances of classes that have these subtle differences is very confusing.

@CTOverton
Copy link

@paul-hart I'm facing a similar issue where in React Native my appsFlyer.onDeepLink function is only being called for deeplinks via URI scheme but not universal links.

My AppDelegate.swift has this function implemented

  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Swift.Bool {
    var result = authorizationFlowManagerDelegate?.resumeExternalUserAgentFlow(with: url) ?? false

    if (!result) {
      AppsFlyerLib.shared().handleOpen(url, options: options)

      result = RCTLinkingManager.application(app, open: url, options: options)

      if (result) {
        UAMessageCenter.shared()?.defaultUI.dismissMessageCenter(animated: true);
      }
    }

    return result;
  }

I tried to add your suggestion to this function

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

But I have the error AppsFlyerAttribution is not found. Where would you be importing that from?

@paul-hart
Copy link

@paul-hart I'm facing a similar issue where in React Native my appsFlyer.onDeepLink function is only being called for deeplinks via URI scheme but not universal links.

My AppDelegate.swift has this function implemented

  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Swift.Bool {
    var result = authorizationFlowManagerDelegate?.resumeExternalUserAgentFlow(with: url) ?? false

    if (!result) {
      AppsFlyerLib.shared().handleOpen(url, options: options)

      result = RCTLinkingManager.application(app, open: url, options: options)

      if (result) {
        UAMessageCenter.shared()?.defaultUI.dismissMessageCenter(animated: true);
      }
    }

    return result;
  }

I tried to add your suggestion to this function

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

But I have the error AppsFlyerAttribution is not found. Where would you be importing that from?

Just noticed this message sorry. It was from:
import react_native_appsflyer

@paul-hart
Copy link

paul-hart commented Apr 2, 2024

Oddly enough I had to work on this part of the code almost exactly one year later. Established the following:

  • AppsFlyerAttribution is just a wrapper class for AppsFlyerLib that checks if the bridge is ready. It holds calls until after the bridge is loaded if it wasn't ready.
  • The latest version of AppsFlyer causes a double header bug when importing from react_native_appsflyer and you have to start using AppsFlyerLib as the import exclusively.
  • Since you can't import react_native_appsflyer without a build error, if you need to wait for the bridge just copy their class files for AppsFlyerAttribution or make something similar of your own:
    - (void)handleOpenUrl:(NSURL *)url options:(NSDictionary *)options{

@gsantiago
Copy link

Any news on this?

I'm facing the same problem. The deeplink doesn't work when the ios app is fully closed.

@gsantiago
Copy link

@annasychugina did you find a solution for this?

@vladimir-d-1
Copy link

vladimir-d-1 commented May 14, 2024

guys, try add this:


   func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
....
        if let userActivity = connectionOptions.userActivities.first {
            self.scene(scene, continue: userActivity)
        }
    }

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        AppsFlyerLib.shared().continue(userActivity)
 }

@Falco26
Copy link

Falco26 commented May 14, 2024

guys, try add this:


   func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
....
        if let userActivity = connectionOptions.userActivities.first {
            self.scene(scene, continue: userActivity)
        }
    }

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        AppsFlyerLib.shared().continue(userActivity)
 }

Where did you add this?

@vladimir-d-1
Copy link

guys, try add this:


   func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
....
        if let userActivity = connectionOptions.userActivities.first {
            self.scene(scene, continue: userActivity)
        }
    }

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        AppsFlyerLib.shared().continue(userActivity)
 }

Where did you add this?

insert this into your SceneDelegate. Info about clicked deeplink pass into the app through connectionOptions param. Previously this info passed into AppDelegate didFinishLaunching... method.

@cvlehe
Copy link

cvlehe commented May 16, 2024

This is caused by iOS functions (application:open url and application:continue userActivity) being called immediately after the app is opened and data being passed to AppsFlyerLib before React Native is initialized and the openDeepLink listener is set.

Switching to AppsFlyerAttribution actually waits for React Native to be initialized, then passed the data to onDeepLink, but unfortunately in Swift, due to the issue mentioned above (#223 (comment)) a double header bug was introduced recently, and now you can only import AppsFlyerLib which doesn't give you access to AppsFlyerAttribution and causes the Cannot find 'AppsFlyerAttribution' in scope error.

To get around this I added #import "RNAppsFlyer.h" to my Bridging-Header file, instead of importing anything into me AppDelegate. I was able to get access to AppsFlyerAttribution and everything worked as expected, including tapping on a deep link after force closing the app and onDeepLink being called. Hope this helps!

AppDelegate

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        AppsFlyerAttribution.shared().handleOpen(url, options: options)
        return RCTLinkingManager.application(app, open: url, options: options)
    }
        
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        // need to add custom handler to avoid type errors on restoration handler
        let appsFlyerRestorationHandler: ([Any]?) -> Void = { restoredItems in
            restorationHandler(restoredItems as? [UIUserActivityRestoring])
        }
        AppsFlyerAttribution.shared().continue(userActivity, restorationHandler: appsFlyerRestorationHandler)
        return RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
    }

@Falco26
Copy link

Falco26 commented May 20, 2024

This is caused by iOS functions (application:open url and application:continue userActivity) being called immediately after the app is opened and data being passed to AppsFlyerLib before React Native is initialized and the openDeepLink listener is set.

Switching to AppsFlyerAttribution actually waits for React Native to be initialized, then passed the data to onDeepLink, but unfortunately in Swift, due to the issue mentioned above (#223 (comment)) a double header bug was introduced recently, and now you can only import AppsFlyerLib which doesn't give you access to AppsFlyerAttribution and causes the Cannot find 'AppsFlyerAttribution' in scope error.

To get around this I added #import "RNAppsFlyer.h" to my Bridging-Header file, instead of importing anything into me AppDelegate. I was able to get access to AppsFlyerAttribution and everything worked as expected, including tapping on a deep link after force closing the app and onDeepLink being called. Hope this helps!

AppDelegate

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        AppsFlyerAttribution.shared().handleOpen(url, options: options)
        return RCTLinkingManager.application(app, open: url, options: options)
    }
        
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        // need to add custom handler to avoid type errors on restoration handler
        let appsFlyerRestorationHandler: ([Any]?) -> Void = { restoredItems in
            restorationHandler(restoredItems as? [UIUserActivityRestoring])
        }
        AppsFlyerAttribution.shared().continue(userActivity, restorationHandler: appsFlyerRestorationHandler)
        return RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
    }

@cvlehe could you please share your full implementation please?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests