Flutterfire: [firebase_dynamic_links] Not working on app start

Created on 6 Sep 2019  Â·  44Comments  Â·  Source: FirebaseExtended/flutterfire

Describe the bug
When opening an iOS App from a dynamic link the initialLink is nil.

To Reproduce
Steps to reproduce the behavior:

  1. Set up firebase dynamic links for a project as described in the readme.
  2. Create a dynamic link (programmatically or via console)
  3. Tap on link in non-browser app

Expected behavior
App should open and getInitialLink() should return the correct dynamic link.

Additional context
From my understanding there is an issue when reacting to the incoming UserActivity. onInitialLink is correctly called but returns way earlier than self.initialLink is set in the completion block from [[FIRDynamicLinks dynamicLinks]handleUniversalLink: completion: ]. Therefore on handling the method call FirebaseDynamicLinks#getInitialLink [self getInitialLink] returns nil and because of self.flutterError also being nil the result returns just with nil.

customer dynamic_links bug

Most helpful comment

@MaciDE Have you found a way to fix this issue or a workaround?
Currently experiencing this issue myself.

Currently, I am calling getInitialLink using a delayed future. It is not working 100%, but at least it is a workaround for now.

PendingDynamicLinkData data;

if (Platform.isIOS) {
  await Future.delayed(Duration(seconds: 2), () async {
     data = await FirebaseDynamicLinks.instance.getInitialLink();
  });
} else {
  data = await FirebaseDynamicLinks.instance.getInitialLink();
}

All 44 comments

@MaciDE Have you found a way to fix this issue or a workaround?
Currently experiencing this issue myself.

@MaciDE Have you found a way to fix this issue or a workaround?
Currently experiencing this issue myself.

Currently, I am calling getInitialLink using a delayed future. It is not working 100%, but at least it is a workaround for now.

PendingDynamicLinkData data;

if (Platform.isIOS) {
  await Future.delayed(Duration(seconds: 2), () async {
     data = await FirebaseDynamicLinks.instance.getInitialLink();
  });
} else {
  data = await FirebaseDynamicLinks.instance.getInitialLink();
}

I've pushed a PR with the fix, can you try? You just need to point to the PR version.

  firebase_dynamic_links:
    git:
      url: https://github.com/mrmilu/flutterfire.git
      path: packages/firebase_dynamic_links
      ref: firebase_dynamic_links-0.5.0+4

I've pushed a PR with the fix, can you try? You just need to point to the PR version?

  firebase_dynamic_links:
    git:
      url: https://github.com/mrmilu/flutterfire.git
      path: packages/firebase_dynamic_links
      ref: firebase_dynamic_links-0.5.0+4

I'll try it next week. So far it looks pretty promising :)

@jherencia will test the PR tomorrow.

@jherencia Your PR solves my issues! Been tinkering with this for 3 days straight, thanks so much!

@jherencia I can confirm your PR fix the problem on iOS when app is in "killed state"

@jherencia Yep, seems like your PR fixes the issue. Thank you!

I just realize it is not working when app is installed for the first time by touching a dynamic link... getInitialLink() is returning always null. Did anyone solve this?

@quetool did not try it trough the App Store yet

@HofmannZ doesn't matter app store, you can simulate app installation by running the project without the app installed. The steps are...

  1. Uninstall app
  2. Tap on a dynamic link
  3. Wait for App Store to open
  4. With App Store opened run your project and wait.

Dynamic link should work after installation (like in Android do)

@jherencia After extensively testing your PR, I can confirm that your fix is working as expected.

The dynamic link is now working when the (closed) App is opened through a dynamic link without delaying the call of FirebaseDynamicLinks.instance.getInitialLink();.
For me it is also working when the app is installed through a dynamic link. The problem mentioned by @quetool should be a problem with the use of a custom domain as he already found out.

@MaciDE

Great to hear, thank you!

@MaciDE yep, the issue I have is when using custom domains... Using default domains like page.link is quite ugly for marketing campaigns... I will try to find a solution...

I think that in that case (with custom domains) the problem is not related with this library, it's probably related to something wrong in the configuration.

@jherencia that's right, I checked with original version of the plugin and with your PR, none of them worked out in this case... Seems to be an old firebase issue, maybe some methods are missings in the iOS side of the plugin, don't really know....

So, I finally find a solution. Pretty dumb indeed... The problem was somehow AppDelegate.m in my project was created (automatically or not, don't really know) with [UIApplicationDelegate application:openURL:options:] function already in it but empty inside...
For some reason this function was "overriding" the one in the plugin so that one in the plugin was never calling...
I addition, I needed to add in [UIApplicationDelegate application:didFinishLaunchingWithOptions:] this calls...

[FIROptions defaultOptions].deepLinkURLScheme = @"my-custom-scheme";
[FIRApp configure];

The first one in order to work with customs schemes, and the second one because for some reason [FIRApp configure] is not being called inside the plugin (don't know if it is a bug or no)...

This is AppDelegate.m now

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@import Firebase;
@implementation AppDelegate

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

    [FIROptions defaultOptions].deepLinkURLScheme = @"my-custom-scheme";
    [FIRApp configure];
    [GeneratedPluginRegistrant registerWithRegistry:self];

    // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

//- (BOOL)application:(UIApplication *)application
//            openURL:(NSURL *)url
//            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
//    // Add any custom logic here.
//    
//    return true;
//}

@end

So now I have Dynamic Links fully functional both on iOS and Android thanks to @jherencia with his PR and some research on my own :P

I hope it helps someone in the future...

So what is going on with this issue? Looks like the pr by @jherencia is also in an unsettled state.
Does not really look that great for the open source part of flutter...

I've updated the PR, fireabase_dynamic_links 0.5.0+4 was officially published so the new valid version from my repo is:

  firebase_dynamic_links:
    git:
      url: https://github.com/mrmilu/flutterfire.git
      path: packages/firebase_dynamic_links
      ref: firebase_dynamic_links-0.5.0+5

@jherencia Looking forward to see your pr getting merged and a new version released. It only takes someone authorized pressing the merge button... :)

@jherencia what did you do in this PR?

@quetool

Nothing, I just applied the changes from master branch so it can be merged. They upgraded the url_launcher version (https://github.com/FirebaseExtended/flutterfire/commit/04b7954788ca291e3841877c0f6b6e9b0a8d8ad3) and some change about androidX (https://github.com/FirebaseExtended/flutterfire/commit/31cad01da7450edc80c2b3645096246389553d41)

@jherencia thanks!

@quetool

doesn't matter app store, you can simulate app installation by running the project without the app installed

Does this method works for play store?

@quetool

doesn't matter app store, you can simulate app installation by running the project without the app installed

Does this method works for play store?

Yep, you need to tap on a dlink (tap on OPEN in preview page, it it has) and then run the project without the app installed. The behavior will be the same as the app installation from play store

@jherencia has the fix been pushed upstream? Still need a slight delay on IOS to make it work with 0.5.0+9 version

@kwent

No, the PR is not merged yet (https://github.com/FirebaseExtended/flutterfire/pull/255), and it seems that we do not have any eyes on it :(

Any updates on this?? This PR (https://github.com/FirebaseExtended/flutterfire/pull/255) seems to fix it.

We are reviewing open issues and open PRs will be looked at in due course.

You can use the PR in development following the config suggested here: https://github.com/FirebaseExtended/flutterfire/issues/100#issuecomment-549177936

Any updates on this ? It's been a long time

Any update on this? The delayed init seems not to be working either

Temporary fix
```
class _MyAppState extends State<_MyApp> with WidgetsBindingObserver {
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
DynamicLinksService.initDynamicLinks(context);
}

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
DynamicLinksService.initDynamicLinks(context);
}
}
}
```

It seems this issue is back with firebase_dynamic_links: ^0.6.0+2

Behaviour: The app is properly started by clicking the link, however, FirebaseDynamicLinks.instance.getInitialLink() almost always returns null, but in some cases it works, however not too frequent, probably 1 out of 10 or less.

Conclusion: This indicates that the race condition still prevails and the bug is not properly fixed by the pull request #255.

I tried all the above workarounds, non of it seems to work:

(1) delay FirebaseDynamicLinks.instance.getInitialLink() like this

      PendingDynamicLinkData data;
      if (Platform.isIOS) {
        await Future.delayed(Duration(seconds: 2), () async {
          data = await FirebaseDynamicLinks.instance.getInitialLink();
        });
      } else {
        data = await FirebaseDynamicLinks.instance.getInitialLink();
      }

      final Uri deepLink = data?.link;

     .....

(2) Calling in main after WidgetsFlutterBinding.ensureInitialized() but before calling runApp, awaiting getInitialLink
(3) Calling in a stateful widget and also delaying in didChangeAppLifecycleState as suggested in earlier workaround (2019)

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    locator.get<DynamicLinkService>().initDynamicLinks(); // getInitialLink and onLink
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      timerLink = new Timer(const Duration(milliseconds: 1000), () async {
          await locator.get<DynamicLinkService>().initDynamicLinks(); // getInitialLink and onLink
        },
      );
    }
  }

I also realised that in a recent commit the order has changed, now the example first calls
onLink to register the handlers and then getInitialLink. This does not make any difference either.

I tested a custom dynamic link and also the Firebase Auth Email link, same behaviour, getInitialLink returns with null.

All tested on physical device. Android works perfectly.

My Flutter version

[✓] Flutter (Channel master, 1.22.0-2.0.pre.93, on Mac OS X 10.15.6 19G2021, locale en-CH)
    • Flutter version 1.22.0-2.0.pre.93 at /Users/dani/development/flutter
    • Framework revision f35a8b7470 (6 weeks ago), 2020-08-26 21:33:03 -0700
    • Engine revision 388193a67b
    • Dart version 2.10.0 (build 2.10.0-62.0.dev)

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
    • Android SDK at /Users/dani/Library/Android/sdk
    • Platform android-30, build-tools 30.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 12.0.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.0.1, Build version 12A7300
    • CocoaPods version 1.9.3

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

[✓] Android Studio (version 4.0)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 46.0.2
    • Dart plugin version 193.7361
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)

[✓] IntelliJ IDEA Ultimate Edition (version 2019.2.4)
    • IntelliJ at /Applications/IntelliJ IDEA.app
    • Flutter plugin version 38.2.4
    • Dart plugin version 192.7761

[✓] VS Code (version 1.49.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.15.0

Happy to do further debugging and diving deeper if someone can give me indications/directions what to do to pinpoint this recurring issue.

@degloff #100 has not been merged, so it is not part of firebase_dynamic_links: ^0.6.0+2.

@jherencia from going through the discussion above it looks like that #255 should have fixed it as mentioned by @tapizquent, which seems to be merged back. There is also a note in the release notes. So what is the status?

I tested Christian's @hffmnn pull request #2580

  firebase_dynamic_links:
    git:
      url: https://github.com/soundreply/flutterfire.git
      path: packages/firebase_dynamic_links
      ref: feature/initial-link-resolving-ios

This seems to fix the issue at least in my setup. As this is a recurring issue open now for a year can someone with the authority press the merge button? Thanks.

@degloff I think there's a common misunderstanding of how the getInitialLink method works and how the initialization should be done. The PR #2580 is a perfect example of this misunderstanding. I'm pretty sure these new reports after the 0.6.0 are mainly caused by this misunderstanding. The 0.6.0 versio has the original bug fixed.

getInitialLink DOES NOT wait for the initial link, and it shouldn't. It only returns the link that may or may not have been received before the onLink listener has been installed.

The initial link may just as well be delivered to the onLink listener. You shouldn't expect to alsways get it from getInitialLink or onLink. It will be delivered by one or the other, but it depends on the timing and pure chance.

So if you first install the onLink listener, and after that call getInitialLink and are prepared to get the link from either, everything works with the current version.

@koskimas thanks for the reply. I observed exactly that change in the 0.6.0 version and i checked it carefully. I commented already on this in my previous comment.

I also realised that in a recent commit the order has changed, now the example first calls
onLink to register the handlers and then getInitialLink. This does not make any difference either.

But it does NOT solve the problem! Also onLink does not fire.

@degloff

But it does NOT solve the problem! Also onLink does not fire.

Works for me every single time on multiple different devices. I think you'll need to show a reproducible example of the fail for people to take this seriously.

@koskimas our app startup sequence is quite complex including some more exotic things like WeChat and Line plugin. This may cause a situation that is unique. All I can confirm is that @hffmnn solution works so far always, whereas the suggested approach is only working in less than 10%. Quite a puzzle.

@koskimas Thank you! This comment helps me.

getInitialLink DOES NOT wait for the initial link, and it shouldn't. It only returns the link that may or may not have been received before the onLink listener has been installed.

I had been falling to get initial link,

    _initialLinkCompleter.complete(component.getInitialLink());

    component.onLink(
      onSuccess: (data) async {
        _linkSub.add(data);
      },
      onError: (e) async {
        recordError(e, StackTrace.current);
      },
    );

but solved by swaping onLink and getInitialLink.

    component.onLink(
      onSuccess: (data) async {
        _linkSub.add(data);
      },
      onError: (e) async {
        recordError(e, StackTrace.current);
      },
    );

    _initialLinkCompleter.complete(component.getInitialLink());

https://pub.dev/packages/firebase_dynamic_links#handle-received-dynamic-links

image

This question has been bothering me all day.
In iOS, I add code in red box, then it worked.

Finally ,my code like this:

  @override
  void initState() {
    super.initState();
    initDynamicLinks();
  }

  void initDynamicLinks() async {
    FirebaseDynamicLinks.instance.onLink(
        onSuccess: (PendingDynamicLinkData dynamicLink) async {
      _checkDynamicLinkParams(dynamicLink);
    }, onError: (OnLinkErrorException e) async {
      Log.d(tag, e.message);
    });
    final PendingDynamicLinkData data =
        await FirebaseDynamicLinks.instance.getInitialLink();
    _checkDynamicLinkParams(data);
  }

  void _checkDynamicLinkParams(PendingDynamicLinkData dynamicLink) {
    // do something...
  }
Was this page helpful?
0 / 5 - 0 ratings