flutterfire
flutterfire copied to clipboard
[firebase_dynamic_links] [iOS] Deeplinks do not work if they are clicked while the app is killed
I have a deeplink that when clicked is supposed to open a specific screen.
On Android all scenarios work perfectly.
On iOS I see the following behaviours:
Working scenario: If the app was already opened, but was put in background, if I click the deeplink, it works fine, the app is put to foreground and shows my custom screen for the opened deeplink path. These are the steps:
- Open my app normally (tap app icon)
- Put the app to background (I just dissmiss my app to go to iOS main menu, note: I do not kill my app from memory)
- I go and open another app where I have my deeplink (eg. Messages or Mail)
- Tap the deeplink
- My app is brought to foreground and the correct screen is shown according to the tapped deeplink path
Broken scenario:
- Kill my app from memory (from app switcher, swipe up on my app so it's killed)
- I go and open another app where I have my deeplink (eg. Messages or Mail)
- Tap the deeplink
- My app opens on the first default screen, the custom screen for the deeplink is not shown (other way to put it: my handler for the deeplink is not called,
onLink/getInitialLinknot called)
Minimal code to reproduce:
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: FirebaseOptions(
apiKey: 'apiKey',
appId: 'appId',
messagingSenderId: 'messagingSenderId',
projectId: 'projectId',
authDomain: 'authDomain',
databaseURL: 'databaseURL',
storageBucket: 'storageBucket',
measurementId: 'measurementId',
androidClientId: 'androidClientId',
iosClientId: 'iosClientId',
iosBundleId: 'iosBundleId',
),
);
runApp(MyApp());
}
my_app.dart
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class MyApp extends StatefulWidget {
MyApp({
Key key,
}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
FirebaseDynamicLinks.instance.onLink.listen(_handleDeepLink);
_getInitialLink();
}
Future<void> _getInitialLink() async {
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
_handleDeepLink(data);
}
@override
Widget build(BuildContext context) {
// return app widgets...
}
Future<dynamic> _handleDeepLink(PendingDynamicLinkData linkData) async {
final Uri deepLink = linkData?.link;
if (deepLink == null) {
return;
}
if (deepLink.path == '/my/custom/path') {
Navigator.of(context).pushNamed(MyCustomScreen.routeName);
}
}
}
flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.10.3, on macOS 12.4 21F79 darwin-arm, locale en-RO)
[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
[✗] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome)
! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[✓] Android Studio (version 2021.2)
[✓] Android Studio (version 2021.2)
[✓] IntelliJ IDEA Community Edition (version 2022.1.3)
[✓] IntelliJ IDEA Community Edition (version 2022.1.2)
[✓] IntelliJ IDEA Ultimate Edition (version EAP IC-222.3244.4)
[✓] IntelliJ IDEA Ultimate Edition (version EAP IC-222.3048.13)
[✓] VS Code (version 1.69.0)
[✓] Connected device (1 available)
[✓] HTTP Host Availability
Firebase libs:
firebase_core: 1.19.2
firebase_dynamic_links: 4.3.2
Thanks for the detailed report @tudor07
4. (other way to put it: my handler for the deeplink is not called,
onLink/getInitialLinknot called)
Does this mean getInitialLink() is null and onLink() is not called at all ?
Are you using dart initialization for your project or manual setup using google services file (GoogleService-Info.plist) ?
If the latter, are you using <key>FirebaseDynamicLinksCustomDomains</key> in info.plist file ?
- Yes -
getInitialLinkisnull,onLinknot called - Yes - I use Dart initialization (no
.plistfile) - Yes, I have that entry in the
Info.plistfile
Thanks for the update. Can you take a look at this solution and see if it works in your case ?
Also check this comment from team member indicating that initializing with flutterfire configure may not support all firebase products yet, so maybe you can try the same scenario using GoogleService-Info.plist ?
I already had the Firebase Installations API added to my API key.
Using GoogleService-Info.plist is not an option for my project.
From your flutter doctor, I see that you are on 2.10.3. Can you possibly upgrade to latest stable and try ?
Also, what iOS version are you seeing this on ? I am trying to know if this is iOS specific or not.
I'm using iOS 15.5
@tudor07 From the code sample you shared earlier, specially below section:
Future<void> _getInitialLink() async {
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
_handleDeepLink(data);
}
Future<dynamic> _handleDeepLink(PendingDynamicLinkData linkData) async {
final Uri deepLink = linkData?.link;
if (deepLink == null) {
return;
}
if (deepLink.path == '/my/custom/path') {
Navigator.of(context).pushNamed(MyCustomScreen.routeName);
}
}
Can you try to first check if linkData is null and if not, what it prints and then something like below ?
if (linkData != null) {
print (`initial link $linkData`);
}
final Uri deepLink = linkData?.link;
if (deepLink != null) {
// your routing logic here
}
I have had the same problem. However, once I tested it with a real device instead of the simulator the initial link wasn't null. @darshankawar It would be nice if you could make it work on the simulator too though :)
@matthewfx I tested on a real device
@tudor07 uh oh... in that case I wonder why it sometimes works and sometimes doesn't... Quite concerning to be honest :(
@darshankawar linkData is null
Thanks for the update. Using the code sample provided, I am getting same behavior.
I've just tested this with and without the GoogleService-Info.plist file, and it works when the app is killed (i.e. the deep link is present). I'm going to update the Dynamic Links example app so you can test it for yourself (double check what possible config you may have set incorrectly) as there a few things that need updating. A couple of things that are necessary:
- It needs to be run in release mode (i.e.
flutter run --release) if you wish to test it from a terminated app state. - It needs to run on an actual device.
@russellwheatley so you are saying that you can't reproduce this issue?
@tudor07 Yes.
I have updated the dynamic link example app. I think you should be able to run it on a device and check. Check out this branch locally: https://github.com/firebase/flutterfire/pull/9250
Here is the dynamic link: https://reactnativefirebase.page.link/bFkn
Steps:
- Clone FlutterFire repo and run
melos bootstrapin the root of project. - Root to dynamic link example app in terminal and run example app in release mode:
flutter run --no-pub --releaseon an actual iOS device. - Kill the app and click on the above noted link.
- The app should open up and immediately take you to a screen via
onLinkhere - The screen simply has the text node "Hello, World!" which is this widget here
I'll check but @darshankawar said he is also able to reproduce my issue, it would be great if he can also try the above example. It would be helpful to try on as many devices as we can.
I'll re-verify and confirm.
@tudor07
Based on @russellwheatley's comment earlier, I verified by running firebase_dynamic_links plugin example in release mode on iOS device (iphone 6s, OS 15.3.1) and was able to see expected behavior. See below:
https://user-images.githubusercontent.com/67046386/182354969-a81864af-9222-45da-a35d-983b106fe49d.MP4
I am on latest master version.
I might be doing something wrong. When I press the link this is what I see:

Screen recording:
https://user-images.githubusercontent.com/7441453/182387316-704edae1-c4da-471d-bc9d-dc9f414d86fc.mov
@tudor07 - did you try this link?
reactnativefirebase.page.link/bFkn
Yes, same with that one
@tudor07 You're using this branch https://github.com/firebase/flutterfire/pull/9250?
Could you also change the default browser as well to chrome and see if that makes a difference.
I am using @russell/dynamic-9110 branch:
❯ git branch
* @russell/dynamic-9110
I get the same behaviour with Chrome.
Btw, this behaviour is for links generated inside the app, if I use reactnativefirebase.page.link/bFkn specifically I get to invertase.io/helloworld which is a 404 page.
If you get the link invertase.io/helloworld then it worked correctly. That is the deeplink:

It is a 404 hence why we use just the path to push you to another screen to show you it works: https://github.com/firebase/flutterfire/blob/master/packages/firebase_dynamic_links/firebase_dynamic_links/example/lib/main.dart#L60
I'm working on two apps at the moment and I spotted the same problem in one of them :v
That's weird, because scenarios when app is installed from app store or link is clicked when the app is in background works fine. Problem happens only when the app is terminated.
I also compared the two apps configs few times and I couldn't spot anything suspicious. The same plugin versions, the same firebase project, two different custom domains
I performed two tests with usage of app_links package on my apps. Both scenarios were launched by clicking dynamic link from terminated state. There was release mode and this launch option checked in XCode:

Code that I've used in both apps:
FirebaseDynamicLinks.instance
.getInitialLink()
.then((value) => print('getInitialLink: $value'))
.onError((error, stackTrace) => print(error));
_appLinks.getInitialAppLink().then((value) {
print('app link: $value');
if (value != null) {
try {
FirebaseDynamicLinks.instance.getDynamicLink(value).then(
(dynamicLink) => print('app link as dynamic: $dynamicLink'));
} catch (e) {
print(e);
}
}
});
FirebaseDynamicLinks.instance.onLink
.listen((event) async => print('onLink: $event'));
And there are results:
Working app:

Not working. You can see that onLink is not called, but the app_links plugin works fine:

Any ideas where I can look for the problem cause?
@russellwheatley shouldn't it open the app? My issue was about opening the app and getting the link that was clicked in Dart
@tudor07 Yes, it will open your app, and the deep link received is the one I mentioned.