Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

jmagman
Copy link
Member

@jmagman jmagman commented Aug 3, 2021

Support deep linking/route pushing via universal links, which are registered http and https scheme URLs, in addition to the already-supported custom schemes.

If the URL isn't handled by the life cycle delegate, and isn't from web browsing, send pushRoute to the app.
Share the open link/routing logic between -application:continueUserActivity:restorationHandler: and -application:openURL:options:.

Fixes flutter/flutter#82550

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides].
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See [testing the engine] for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the [CLA].
  • All existing and new tests are passing.

@jmagman jmagman self-assigned this Aug 3, 2021
@google-cla google-cla bot added the cla: yes label Aug 3, 2021
openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options
infoPlistGetter:(NSDictionary* (^)())infoPlistGetter {
if ([_lifeCycleDelegate application:application openURL:url options:options]) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is now shared between two app delegate methods. Move the method-specific _lifeCycleDelegate check up one level to -application:openURL:options:.

infoPlistGetter:^NSDictionary*() {
return [[NSBundle mainBundle] infoDictionary];
}];
if ([_lifeCycleDelegate application:application openURL:url options:options]) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(it was moved here)

@jmagman jmagman requested a review from gaaclarke August 4, 2021 17:17
Comment on lines 233 to 226
if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
return NO;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not catching why this is here. It sounds like NSUserActivityTypeBrowsingWeb is associated with universal links? Wouldn't you want this? I see this behavior matches what you want in your test so I assume it's true, I'm just not familiar with the API.

Maybe a few words as to why we return NO here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is a good question. @AlBlanc I noticed you did you did this in #25705 and meant to follow up. Why did you avoid this activity type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason to not process NSUserActivityTypeBrowsingWeb so I removed this and the test.
https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app

We can always add it back later if there's some good reason to.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds fine with me, we could wait a bit more to give @AlBlanc 24 hours to respond. He's probably celebrating the France basketball teams win =)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking I can merge this now and put the additional PR if should be excluded. Based on my reading of https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app it shouldn't be.
But yeah I can wait too, it's not a rush...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to merge this. I'll change it if we hear we need to exclude NSUserActivityTypeBrowsingWeb for some reason.

OCMReject([navigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]);
}

- (void)testUniversalLinkPushRoute {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 tests are very similar, maybe it's worth considering pulling out a helper function to remove some of the boilerplate so the differences are more apparent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled the boilderplate in all these tests up into setUp. Hopefully it's easier to read now.

- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options
infoPlistGetter:(NSDictionary* (^)())infoPlistGetter;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wound up getting rid of this test injection helper and mocking out -[NSBundle objectForInfoDictionaryKey:] instead.

OCMReject([navigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]);
}

- (void)testUniversalLinkPushRoute {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled the boilderplate in all these tests up into setUp. Hopefully it's easier to read now.


// Retain callback until the tests are done.
// https://github.com/flutter/flutter/issues/74267
@property(strong) id mockEngineFirstFrameCallback;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only just landed #27854 but this is probably a better name than blockNoInvoker. Open to suggestions though!

return YES;
} else if (!IsDeepLinkingEnabled(infoPlistGetter())) {
- (BOOL)openURL:(NSURL*)url {
NSNumber* isDeepLinkingEnabled =
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to get rid of the static function here. -[NSBundle objectForInfoDictionaryKey:] is now mocked in the tests.

arguments:@"/custom/route?query=test"]);
}

- (void)testLaunchUrlWithDeepLinkingNotSet {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New test.

OCMReject([self.mockNavigationChannel invokeMethod:OCMOCK_ANY arguments:OCMOCK_ANY]);
}

- (void)testLaunchUrlWithDeepLinkingDisabled {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New test.

@jmagman jmagman added the waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. label Aug 9, 2021
@fluttergithubbot fluttergithubbot merged commit 266435d into flutter:master Aug 9, 2021
@jmagman jmagman deleted the universal-links branch August 10, 2021 20:05
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Aug 11, 2021
@vasilich6107
Copy link

Hi @jmagman
Could you clarify what version of flutter includes this fix?

@jmagman
Copy link
Member Author

jmagman commented Oct 6, 2021

Hi @jmagman Could you clarify what version of flutter includes this fix?

2.6.0-0.0.pre

@vasilich6107
Copy link

vasilich6107 commented Oct 7, 2021

Hi @jmagman
I installed 2.6.0 as you recommended.

The deep links works fine when I tap the link from email app.

The issue is if I attach the link to push notification and tap on incoming push message -> it still opens the webview.

Is this ok for current implementation?

@jmagman
Copy link
Member Author

jmagman commented Oct 7, 2021

The issue is if I attach the link to push notification and tap on incoming push message -> it still opens the webview.

@vasilich6107 can you file a new issue with reproduction steps?

@vasilich6107
Copy link

No problem. Will do

@janjoosse
Copy link

@vasilich6107 What setup and configuration did you do to get the deep links working (for links from an email)?

@vasilich6107
Copy link

@janjoosse thanks for support
Will have time to fire properly described issue on Thursday

@ikurek
Copy link

ikurek commented Nov 15, 2021

@vasilich6107 Did you open the issue? I'm also having problems with deep links when they are in foreground, after deep link is fired (either from push notification or CLI) RouteInformationParser and RouteInformationProvider do not receive any kind of information about route change (although they do on Android). May be the same thing as in your case

@dragouf
Copy link

dragouf commented Feb 19, 2022

I'm using flutter 2.10.1 and when I request:
/usr/bin/xcrun simctl openurl booted "https://mydomain.com/mypath"

it open the app but the view is not push based on the path as expected (it works perfectly on android).

Is this still working in 2.10 ? FlutterDeepLinkingEnabled is set to true for ios. It's what I need to get the view automatically pushed right ? I use onGenerateRoute for named routes.

EDIT: by removing uni_links lib from my project it now works !

@percula
Copy link

percula commented Feb 21, 2022

@dragouf Are the instructions at https://docs.flutter.dev/development/ui/navigation/deep-linking#enable-deep-linking-on-ios still valid for universal links? I don't see why we need the custom scheme. Do we still need the CFBundleURLTypes entry for universal links?

@dragouf
Copy link

dragouf commented Feb 21, 2022

@percula what they call deep links is not universal links. Deep links use custom scheme when universal links (or app links for android) use https.

If you want to use https instead of custom scheme follow the readme of this library to activate it: https://pub.dev/packages/uni_links

but do not install the library if you want the view to be pushed automatically based on the link path (the library seems to conflicts with this mechanism).

so after I enabled universal links I use named routes with something like:

MaterialApp(onGenerateRoute: AppRouter.onGenerateRoute)

and

class AppRouter {
  static Route<dynamic> onGenerateRoute(RouteSettings settings) {
    if (settings.name != null && settings.name!.startsWith('/thepath/') {
      return CupertinoPageRoute(
        fullscreenDialog: true,
        builder: (_) => const MyWidgetView(),
      );
    }
}

settings.name will contain the path from your link.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes platform-ios waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

iOS Universal Links for Navigator2.0/Router with FlutterDeepLinkingEnabled
8 participants