flutterfire icon indicating copy to clipboard operation
flutterfire copied to clipboard

🐛 [firebase_dynamic_link] New iOS install doesn't open with dynamic link after install

Open badrobot15 opened this issue 3 years ago • 13 comments
trafficstars

Bug report

Dynamic Link returns null On latest version of 4.0.2, for fresh app installs from a dynamic link in iOS via AppStore, the link isn't received in Flutter App. On clicking the link, the app store page opens as required, but after the user downloads the app, the dynamic link within the app seems to be null. Now if having the app already installed on the phone, on clicking the link, the app successfully gets the link. While this behavior is fine for users who already have the app, it totally ruins the experience for new users.

Additional context

Found a similar problem at issue #1861 but that seems to have been closed with the only solution being restart the phone and then install. Since the only way we are able to reproduce the bug is after deploying it to production on app store, it is quite difficult to debug and find the problem.

         final _appLinks = AppLinks(
         onAppLink: (Uri deepLink, String dataStr) async {
           
           if (deepLink == null) return false;

           pendingDynamicLinkData = await FirebaseDynamicLinks.instance.getDynamicLink(deepLink);
           actualDeepLink = pendingDynamicLinkData.link ?? null; 

           if (actualDeepLink == null) return false;
           .
           .
           .
           .
           return true;

         },
       );

As per #6913 I had to use AppLinks to get the link for iOS only.

badrobot15 avatar Dec 10 '21 13:12 badrobot15

@badrobot15 Does getInitialLink() get back with data or is null throughout the case ? Also, is there a way for you to check if ibi parameter matches with your app's bundle Id ?

darshankawar avatar Dec 14 '21 10:12 darshankawar

@darshankawar getInitialLink() comes null throughout the case even when app is intalled on phone hence the need to use app_links package. ibi parameter matches with apps's bundle id.

As per the flowchart here, getInitialLink() doesn't work for when the app is installed nor for when the user is sent to the app store to install the app. However, the dynamic link is copied to clipboard when user is redirected to app store.

After switching to app_links package we found that if the app is installed, the link is delivered successfully by the code snippet I posted above

Following is the initial code we tried

final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();

final Uri deepLink = data?.link;

FirebaseDynamicLinks.instance.onLink.listen((event)  {

        pendingDynamicLinkData = event;

        actualDeepLink = pendingDynamicLinkData.link ?? null;

        if (actualDeepLink == null) return false;
        ...
        ...
        return true;
      },

     onError: (error){
          
     },

     onDone: (){

     },

);

badrobot15 avatar Dec 14 '21 12:12 badrobot15

Thanks for the update. Labeling it based on the report and the fact that this is replicable only after deploying prod app on appstore and then trying to install the app.

/cc @Salakar

darshankawar avatar Dec 15 '21 10:12 darshankawar

flutter doctor -v

[✓] Flutter (Channel stable, 2.10.4, on macOS 12.3.1 21E258 darwin-arm, locale en-IN)
    • Flutter version 2.10.4 at /Users/snghnishant/Developer/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision c860cba910 (5 weeks ago), 2022-03-25 00:23:12 -0500
    • Engine revision 57d3bac3dd
    • Dart version 2.16.2
    • DevTools version 2.9.2

[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
    • Android SDK at /Users/snghnishant/Library/Android/sdk
    • Platform android-31, build-tools 31.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7772763)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 13.3.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2021.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7772763)

[✓] VS Code (version 1.66.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension can be installed from:
      🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected device (1 available)
    • Chrome (web) • chrome • web-javascript • Google Chrome 100.0.4896.127

[✓] HTTP Host Availability
    • All required HTTP hosts are available

• No issues found!

I have tried implementing with the latest plugin firebase_dynamic_links: ^4.2.1 and both onLink.listen() getInitialLink() don't seem to work on iOS though Android works as expected without any issue.

Also tried implementing the workaround given in the related issue using app_links: ^3.1.0 but this too not working at all on iOS.

Here's the implementation of the code (I have separate functions for handling terminated and background/foreground redirects in our app

Future<void> handleTerminatedStateDynamicLinks(
    BuildContext context, FirebaseDynamicLinks dynamicLinkInstance) async {
  try {
    final Uri? deepLink;
    // Terminated app state
    /*if (Platform.isIOS) {
      AppLinks _appLinks = AppLinks();
      deepLink = await _appLinks.getInitialAppLink();
      debugPrint("Deeplink URL (iOS terminated): ${deepLink.toString()}");
    } else {
      final PendingDynamicLinkData? data =
          await dynamicLinkInstance.getInitialLink();
      deepLink = data?.link;
      debugPrint("Deeplink URL (on terminated): ${deepLink.toString()}");
    }*/
    final PendingDynamicLinkData? data =
        await dynamicLinkInstance.getInitialLink();
    deepLink = data?.link;
    debugPrint("Deeplink URL (on terminated): ${deepLink.toString()}");
    if (deepLink != null) {
      String? referCode = deepLink.queryParameters['invited_by'];
      // Store refer code in hive because user can drop off in b/w signup
      if (referCode != null) {
        bool flag = await putPersistentData('refer_code', referCode);
        if (flag) {
          debugPrint("Saved Referral Code to Hive");
          CleverTapPlugin.recordEvent('Referral App Open', {});
        }
      } else {
        // store deeplink for redirect
        await putPersistentData('deepLink', deepLink.toString());
      }
    }
    return;
  } catch (error) {
    debugPrint("Dynamic link terminated state error: ${error.toString()}");
    sendErrorLog("handleTerminatedStateDynamicLinks: ${error.toString()}");
  }
}

Future<void> handleBackgroundForegroundStateDynamicLinks(
    BuildContext context, FirebaseDynamicLinks dynamicLinkInstance) async {
  /*if (Platform.isIOS) {
    final AppLinks _appLinks = AppLinks();

    // Foreground or background app state [iOS]
    _appLinks.uriLinkStream.listen((uri) async {
      debugPrint('iOS onAppLink: $uri');
      PendingDynamicLinkData? deepLink =
          await dynamicLinkInstance.getDynamicLink(uri);
      handleDynamicLinksRedirects(context, deepLink?.link);
    });
  } else {*/
  // Foreground or background app state [ANDROID]
  dynamicLinkInstance.onLink.listen((dynamicLinkData) async {
    final Uri deepLink = dynamicLinkData.link;
    debugPrint(
        "Deeplink URL (on foreground/background): ${deepLink.toString()}");
    handleDynamicLinksRedirects(context, deepLink);
    return;
  }).onError((error) {
    // Handle errors
    debugPrint(
        "Dynamic Link error at foreground/background Listener: ${error.toString()}");
    sendErrorLog(
        "handleBackgroundForegroundStateDynamicLinks: ${error.toString()}");
  });
  //}
  return;
}

snghnishant avatar Apr 28 '22 19:04 snghnishant

I have the same problem =(

otopba avatar Jun 01 '22 08:06 otopba

@darshankawar As discussed in https://github.com/firebase/flutterfire/issues/9103#issuecomment-1191251679 I'm posting my question here.

This is the exact issue I am facing. I have already tried the workaround using the app_links plugin but it didn't solve the issue. Though I have only tested through TestFlight using the iosFallbackLink (pointing to the TestFlight app). Can you confirm testing dynamic links through TestFlight using the iosFallbackLink parameter should work?

wederchr avatar Jul 21 '22 09:07 wederchr

@darshankawar Any update on this issue?

wederchr avatar Jul 26 '22 10:07 wederchr

@darshankawar A response would be much appreciated. I have not heard back from you since you closed my original issue.

wederchr avatar Aug 02 '22 08:08 wederchr

@wederchr Sorry for not coming back to this earlier. Please take a look at below links and see if they help:

https://github.com/firebase/flutterfire/issues/8926 https://github.com/firebase/flutterfire/issues/8926#issuecomment-1196653932 https://github.com/firebase/flutterfire/issues/9110#issuecomment-1198263612 https://github.com/firebase/flutterfire/issues/9110#issuecomment-1201156205

darshankawar avatar Aug 02 '22 10:08 darshankawar

/cc @russellwheatley

darshankawar avatar Aug 02 '22 10:08 darshankawar

@wederchr Sorry for not coming back to this earlier. Please take a look at below links and see if they help:

#8926 #8926 (comment) #9110 (comment) #9110 (comment)

Thank you for your response, I'll take a look! Did you also have time to clarify my question regarding firebase dynamic link testing using TestFlight?

wederchr avatar Aug 02 '22 10:08 wederchr

flutter doctor -v


[✓] Flutter (Channel stable, 2.10.4, on macOS 12.3.1 21E258 darwin-arm, locale en-IN)

    • Flutter version 2.10.4 at /Users/snghnishant/Developer/flutter

    • Upstream repository https://github.com/flutter/flutter.git

    • Framework revision c860cba910 (5 weeks ago), 2022-03-25 00:23:12 -0500

    • Engine revision 57d3bac3dd

    • Dart version 2.16.2

    • DevTools version 2.9.2



[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)

    • Android SDK at /Users/snghnishant/Library/Android/sdk

    • Platform android-31, build-tools 31.0.0

    • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java

    • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7772763)

    • All Android licenses accepted.



[✓] Xcode - develop for iOS and macOS (Xcode 13.3.1)

    • Xcode at /Applications/Xcode.app/Contents/Developer

    • CocoaPods version 1.11.3



[✓] Chrome - develop for the web

    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome



[✓] Android Studio (version 2021.1)

    • Android Studio at /Applications/Android Studio.app/Contents

    • Flutter plugin can be installed from:

      🔨 https://plugins.jetbrains.com/plugin/9212-flutter

    • Dart plugin can be installed from:

      🔨 https://plugins.jetbrains.com/plugin/6351-dart

    • Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7772763)



[✓] VS Code (version 1.66.2)

    • VS Code at /Applications/Visual Studio Code.app/Contents

    • Flutter extension can be installed from:

      🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter



[✓] Connected device (1 available)

    • Chrome (web) • chrome • web-javascript • Google Chrome 100.0.4896.127



[✓] HTTP Host Availability

    • All required HTTP hosts are available



• No issues found!

I have tried implementing with the latest plugin firebase_dynamic_links: ^4.2.1 and both onLink.listen() getInitialLink() don't seem to work on iOS though Android works as expected without any issue.

Also tried implementing the workaround given in the related issue using app_links: ^3.1.0 but this too not working at all on iOS.

Here's the implementation of the code (I have separate functions for handling terminated and background/foreground redirects in our app


Future<void> handleTerminatedStateDynamicLinks(

    BuildContext context, FirebaseDynamicLinks dynamicLinkInstance) async {

  try {

    final Uri? deepLink;

    // Terminated app state

    /*if (Platform.isIOS) {

      AppLinks _appLinks = AppLinks();

      deepLink = await _appLinks.getInitialAppLink();

      debugPrint("Deeplink URL (iOS terminated): ${deepLink.toString()}");

    } else {

      final PendingDynamicLinkData? data =

          await dynamicLinkInstance.getInitialLink();

      deepLink = data?.link;

      debugPrint("Deeplink URL (on terminated): ${deepLink.toString()}");

    }*/

    final PendingDynamicLinkData? data =

        await dynamicLinkInstance.getInitialLink();

    deepLink = data?.link;

    debugPrint("Deeplink URL (on terminated): ${deepLink.toString()}");

    if (deepLink != null) {

      String? referCode = deepLink.queryParameters['invited_by'];

      // Store refer code in hive because user can drop off in b/w signup

      if (referCode != null) {

        bool flag = await putPersistentData('refer_code', referCode);

        if (flag) {

          debugPrint("Saved Referral Code to Hive");

          CleverTapPlugin.recordEvent('Referral App Open', {});

        }

      } else {

        // store deeplink for redirect

        await putPersistentData('deepLink', deepLink.toString());

      }

    }

    return;

  } catch (error) {

    debugPrint("Dynamic link terminated state error: ${error.toString()}");

    sendErrorLog("handleTerminatedStateDynamicLinks: ${error.toString()}");

  }

}



Future<void> handleBackgroundForegroundStateDynamicLinks(

    BuildContext context, FirebaseDynamicLinks dynamicLinkInstance) async {

  /*if (Platform.isIOS) {

    final AppLinks _appLinks = AppLinks();



    // Foreground or background app state [iOS]

    _appLinks.uriLinkStream.listen((uri) async {

      debugPrint('iOS onAppLink: $uri');

      PendingDynamicLinkData? deepLink =

          await dynamicLinkInstance.getDynamicLink(uri);

      handleDynamicLinksRedirects(context, deepLink?.link);

    });

  } else {*/

  // Foreground or background app state [ANDROID]

  dynamicLinkInstance.onLink.listen((dynamicLinkData) async {

    final Uri deepLink = dynamicLinkData.link;

    debugPrint(

        "Deeplink URL (on foreground/background): ${deepLink.toString()}");

    handleDynamicLinksRedirects(context, deepLink);

    return;

  }).onError((error) {

    // Handle errors

    debugPrint(

        "Dynamic Link error at foreground/background Listener: ${error.toString()}");

    sendErrorLog(

        "handleBackgroundForegroundStateDynamicLinks: ${error.toString()}");

  });

  //}

  return;

}



The flutter firebase dynamic links plugin seems to work now without any other package. There was an issue with some native code being override for supporting an attribution SDK in my case. Removing that helped making firebase dynamic link plugin work on both iOS and android in all 3 app state.

snghnishant avatar Aug 02 '22 10:08 snghnishant

Hey @badrobot15, do you still experience this issue?

russellwheatley avatar Aug 02 '22 16:08 russellwheatley

I've released our app with dynamic links implemented for the first time and unfortunately, it doesn't work after installation for us either :( It works great for Android or when the iOS app is already installed... That is very disappointing because deep links will be mainly used for that particular case when the app is freshly installed... Any idea when this bug could be fixed? @russellwheatley

matthewfx avatar Aug 10 '22 09:08 matthewfx

The same for me, dynamic link is not working when the app is not installed and user is redirected to the App Store. Then, when navigating to the newly installed app, dynamic link is not received.

lnevaril avatar Aug 14 '22 16:08 lnevaril

In the most recent release, I've added the app_links package (someone suggested it as a workaround). I've also added remote logging so that when the app is launched it sends me an initial link captured by both Firebase and App_Links. I thought that the worst-case scenario is that app_links catches a universal link and it can't be parsed into a Dynamic link with the getDynamicLink(...). To my surprise, both are null instead... I'm puzzled because it either means that universal links, in general, are not received after installation or that for that particular scenario there is some other way of capturing the dynamic link. I know there is a certain flag ininfo.plistdisabling clipboard. I've disabled it... Anyways, for my next release, I will try a pure universal link with a subdomain not redirected to Firebase. @russellwheatley @darshankawar Could you please look into it? Is there a way of testing it with TestFlight or it always has to be released to the app store? It is so frustrating :(

matthewfx avatar Aug 15 '22 05:08 matthewfx

Hey @badrobot15. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

google-oss-bot avatar Aug 24 '22 01:08 google-oss-bot

I was able to make it work on iOS by adding app_links package and moving the check for initial link further in the application's lifecycle. At first, I was checking initial link after Firebase initialization but now it is moved after user's signup (I'am using is for detecting referral) and it works.

lnevaril avatar Aug 24 '22 07:08 lnevaril

@russellwheatley @darshankawar This issue is far from resolved. If the @badrobot15 is not responding anymore I'd like to ask you to reopen the issue I've reported https://github.com/firebase/flutterfire/issues/9103 which was closed in favor of this one.

wederchr avatar Aug 24 '22 07:08 wederchr

@lnevaril I have a similar setup to yours. app_links and checking it later but it doesn't work for me... :( I wonder what is the difference... @wederchr I think we should open a new one. Seems like since they marked it as blocked by customer-response it no longer gets any attention...

matthewfx avatar Aug 24 '22 08:08 matthewfx

This issue is still open, so no need to open a new issue.

/cc @russellwheatley for further insights on this.

darshankawar avatar Aug 24 '22 11:08 darshankawar

@darshankawar @russellwheatley any updates?

matthewfx avatar Aug 30 '22 12:08 matthewfx

I've integrated https://branch.io and their solution works after installation

matthewfx avatar Aug 31 '22 07:08 matthewfx

Hey @badrobot15. We need more information to resolve this issue but there hasn't been an update in 7 weekdays. I'm marking the issue as stale and if there are no new updates in the next 7 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

google-oss-bot avatar Sep 09 '22 01:09 google-oss-bot

Since there haven't been any recent updates here, I am going to close this issue.

@badrobot15 if you're still experiencing this problem and want to continue the discussion just leave a comment here and we are happy to re-open this.

google-oss-bot avatar Sep 20 '22 01:09 google-oss-bot

Since there haven't been any recent updates here, I am going to close this issue.

@badrobot15 if you're still experiencing this problem and want to continue the discussion just leave a comment here and we are happy to re-open this.

google-oss-bot avatar Sep 21 '22 01:09 google-oss-bot

Im also having an issue here, for android works perfectly but ios has a lot of issues

vascodegraaff avatar Sep 27 '22 21:09 vascodegraaff

Having the same issue. getInitialLink() always null and onLink method seems not called on ios. I also tried app_link package and it always return null.

MrJohn2014 avatar Sep 30 '22 13:09 MrJohn2014

It looks like @darshankawar and @russellwheatley don't care anymore because they keep promising to look into it and then nothing happens for weeks and bots try to either tag it or close it. It is a pity because even though branch.io offers a working alternative I'd prefer to use Firebase because my app has been already having too many dependencies...

matthewfx avatar Oct 03 '22 15:10 matthewfx

Did you also have time to clarify my question regarding firebase dynamic link testing using TestFlight?

I think using ifl parameter as you had already mentioned and verified, can be used which will install the app from testlfight instead of app store.

It looks like @darshankawar and @russellwheatley don't care anymore because they keep promising to look into it and then nothing happens for weeks and bots try to either tag it or close it.

This isn't true. We care for each and every issue that is filed. The team comes back to issues when they have completed the task at their hand. I will also forward this issue for team's attention.

darshankawar avatar Oct 04 '22 05:10 darshankawar