-
Notifications
You must be signed in to change notification settings - Fork 6k
[deep link][ios] Update openURL method to reflect the result from framework #52643
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
338a4e1
to
c762720
Compare
i realized So i guess the return value of it can only reflect the deep link flag value, not the bool result from framework then. |
- (void)openURL:(NSURL*)url | ||
options:(NSDictionary<UIApplicationOpenExternalURLOptionsKey, id>*)options | ||
completionHandler:(void (^)(BOOL success))completion { | ||
if (![self isFlutterDeepLinkingEnabled]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even this is No, we still want to go through plugins, I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why we want to do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for those who use uni-link or their own plugin to handle deeplink, they would set this to no, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes they would set this to no.
but for those using uni-link and didn't set isFlutterDeepLinkingEnabled
flag, the current behavior is already return NO for these methods and don't invoke the framework method. This PR is not changing their experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right, I misread the code, looks like the plugin would have been called already before reaching here.
@@ -171,7 +188,14 @@ - (BOOL)application:(UIApplication*)application | |||
if ([_lifeCycleDelegate application:application openURL:url options:options]) { | |||
return YES; | |||
} | |||
return [self openURL:url]; | |||
if (![self isFlutterDeepLinkingEnabled]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we still ned this check if we go delegate to other selector in line 194?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just add this so the return value reflect the value of the flag.
options:options | ||
completionHandler:^(BOOL success){ | ||
}]; | ||
return YES; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this method get the result from completion handler only?
Also is implementing this selector optional if we already implement the one with completionhandler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this method get the result from completion handler only?>>
this application:openURL:options: method is non-async, i don't think its return value can be set from the completion handler so i used the deep link flag value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But should this method return before the completion handler is complete? Before, this method didn't return until pushRouteInformation
was actually sent, whereas now it would return instantly.
It seems like you'd want to spin the runloop until the completion handler was done, and then return the value from the completion handler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the best practice to "spin the runloop"?
I updated the code to wait for the completion handler but got failures :
Failures for clang-tidy on /Volumes/Work/s/w/ir/cache/builder/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm:
/Volumes/Work/s/w/ir/cache/builder/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm:178:3: error: Waiting on a callback using a semaphore creates useless threads and is subject to priority inversion; consider using a synchronous API or changing the caller to be asynchronous [clang-analyzer-optin.performance.GCDAntipattern,-warnings-as-errors]
178 | dispatch_semaphore_wait(semaphore, 5 * NSEC_PER_SEC);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
- (void)openURL:(NSURL*)url | ||
options:(NSDictionary<UIApplicationOpenExternalURLOptionsKey, id>*)options | ||
completionHandler:(void (^)(BOOL success))completion { | ||
if (![self isFlutterDeepLinkingEnabled]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right, I misread the code, looks like the plugin would have been called already before reaching here.
@@ -149,18 +155,29 @@ - (BOOL)openURL:(NSURL*)url { | |||
if (didTimeout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that this is async, can we get rid of the waitForFirstFrame
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remembered this was added to workaround the issue that when we receive openurl, we don't know what the current state of flutter is in. If we send if before the first frame is drawn, the message will be dropped because the widgetbindingobserver is not yet registered. It is also too late to set initialroute at this point since it is already consumed by the framework.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but this was a long time ago, I am not sure if this is still the case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like FlutterAppDelegate
needs to know a lot of details about the engine, the navigationChannel, and the available method. What if this changed to:
- (void)openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenExternalURLOptionsKey, id>*)options
completionHandler:(void (^)(BOOL success))completion {
if (![self isFlutterDeepLinkingEnabled]) {
completion(NO);
} else {
FlutterViewController* flutterViewController = [self rootFlutterViewController];
if (flutterViewController) {
[flutterViewController openDeepLink:url completionHandler:completion];
} else {
FML_LOG(ERROR) << "Attempting to open an URL without a Flutter RootViewController.";
completion(NO);
}
}
}
FlutterViewController_Internal.h
- (void)openDeepLink:(NSURL*)url completionHandler:(void (^)(BOOL success))completion;
and then in FlutterViewController:
- (void)openDeepLink:(NSURL*)url completionHandler:(void (^)(BOOL success))completion {
[_engine.get()
waitForFirstFrame:3.0
callback:^(BOOL didTimeout) {
if (didTimeout) {
FML_LOG(ERROR) << "Timeout waiting for the first frame when launching an URL.";
completion(NO);
} else {
// invove the method and get the result
[[_engine.get() navigationChannel]
invokeMethod:@"pushRouteInformation"
arguments:@{
@"location" : url.absoluteString ?: [NSNull null],
}
result:^(id _Nullable result) {
BOOL success =
[result isKindOfClass:[NSNumber class]] && [result boolValue];
if (!success) {
// Logging the error if the result is not successful
FML_LOG(ERROR) << "Failed to handle route information in Flutter.";
}
completion(success);
}];
}
}];
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, It does seem like the viewcontroller may be a better place to put these logic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, this looks good, updated!
shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm
Outdated
Show resolved
Hide resolved
shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm
Outdated
Show resolved
Hide resolved
@jmagman ping for review |
// Not set or NO. | ||
return NO; | ||
// if not set, return NO | ||
return isDeepLinkingEnabled ? [isDeepLinkingEnabled boolValue] : NO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this can be just isDeepLinkingEnabled.boolValue
since sending message to nil will return NO
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will keep this and flip NO to YES in #52350
while (!openURLCompleted) { | ||
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; | ||
} | ||
return openURLSuccess; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can you write a helper to avoid duplicate code?
|
||
while (!openURLCompleted) { | ||
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: If you are iffy about using BOOL flag for completeness, a semaphore version can be (pseudocode):
__block BOOL openURLSuccess = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self openURL: url ... {
openURLSuccess = success;
dispatch_semaphore_signal(semaphore)
}];
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
Though a BOOL flag would be equally as good, since callback happens on main thread. I don't have a preference here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I just saw @jmagman's comment on semaphore.
To use semaphore on the same thread, you can't block it due to deadlock, as jenn previously commented. Instead, you pass in DISPATCH_TIME_NOW
in dispatch_semaphore_wait
, and then kick off the runloop so it can pick up the completion handler.
From PR triage: What is the status of this PR? @Hangyujin is this ready for another review pass from @hellohuanlin? |
There's a deadlock issue that I am looking into and will unblock @Hangyujin when it's done. |
Can we close this issue in the meantime? |
return isDeepLinkingEnabled ? [isDeepLinkingEnabled boolValue] : NO; | ||
} | ||
|
||
- (void)openURL:(NSURL*)url |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
follow up from meeting - can delete this.
Some notes from the offline meeting: *Return value: it seems like returning no or yes in *Custom URL scheme: *Add to app : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM after nit
shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm
Outdated
Show resolved
Hide resolved
shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm
Outdated
Show resolved
Hide resolved
shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm
Outdated
Show resolved
Hide resolved
Jenn is OOO this week. Feel free to land it. |
Jenn's OOO + I did a pretty detailed review
…151208) flutter/engine@4427894...c5c0c54 2024-07-03 jacksongardner@google.com Reland "Output .js files as ES6 modules. (#52023)" (flutter/engine#53688) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from d02998fba957 to 7c69f39fa85b (1 revision) (flutter/engine#53701) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from c1f2dd0fc5f6 to d02998fba957 (2 revisions) (flutter/engine#53699) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from c0ee0e108900 to c1f2dd0fc5f6 (2 revisions) (flutter/engine#53697) 2024-07-02 flar@google.com [Impeller] fix typo in setup for fast elliptical rrect blurs (flutter/engine#53673) 2024-07-02 skia-flutter-autoroll@skia.org Roll Fuchsia GN SDK from RgErspyYHapUO2Spc... to sbh76PYVTMxav4ACT... (flutter/engine#53693) 2024-07-02 30870216+gaaclarke@users.noreply.github.com [Impeller] fixed units for memory measurement (flutter/engine#53687) 2024-07-02 jhy03261997@gmail.com [deep link][ios] Update openURL method to reflect the result from framework (flutter/engine#52643) 2024-07-02 skia-flutter-autoroll@skia.org Manual roll Dart SDK from c23e58143793 to ffc8bb004a64 (2 revisions) (flutter/engine#53690) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC aaclarke@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…lutter#151208) flutter/engine@4427894...c5c0c54 2024-07-03 jacksongardner@google.com Reland "Output .js files as ES6 modules. (flutter#52023)" (flutter/engine#53688) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from d02998fba957 to 7c69f39fa85b (1 revision) (flutter/engine#53701) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from c1f2dd0fc5f6 to d02998fba957 (2 revisions) (flutter/engine#53699) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from c0ee0e108900 to c1f2dd0fc5f6 (2 revisions) (flutter/engine#53697) 2024-07-02 flar@google.com [Impeller] fix typo in setup for fast elliptical rrect blurs (flutter/engine#53673) 2024-07-02 skia-flutter-autoroll@skia.org Roll Fuchsia GN SDK from RgErspyYHapUO2Spc... to sbh76PYVTMxav4ACT... (flutter/engine#53693) 2024-07-02 30870216+gaaclarke@users.noreply.github.com [Impeller] fixed units for memory measurement (flutter/engine#53687) 2024-07-02 jhy03261997@gmail.com [deep link][ios] Update openURL method to reflect the result from framework (flutter/engine#52643) 2024-07-02 skia-flutter-autoroll@skia.org Manual roll Dart SDK from c23e58143793 to ffc8bb004a64 (2 revisions) (flutter/engine#53690) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC aaclarke@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Thank you so much for working on this @Hangyujin and for helping out @hellohuanlin, it got much trickier than I expected! |
@jmagman Thank you! |
…lutter#151208) flutter/engine@4427894...c5c0c54 2024-07-03 jacksongardner@google.com Reland "Output .js files as ES6 modules. (flutter#52023)" (flutter/engine#53688) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from d02998fba957 to 7c69f39fa85b (1 revision) (flutter/engine#53701) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from c1f2dd0fc5f6 to d02998fba957 (2 revisions) (flutter/engine#53699) 2024-07-02 skia-flutter-autoroll@skia.org Roll Skia from c0ee0e108900 to c1f2dd0fc5f6 (2 revisions) (flutter/engine#53697) 2024-07-02 flar@google.com [Impeller] fix typo in setup for fast elliptical rrect blurs (flutter/engine#53673) 2024-07-02 skia-flutter-autoroll@skia.org Roll Fuchsia GN SDK from RgErspyYHapUO2Spc... to sbh76PYVTMxav4ACT... (flutter/engine#53693) 2024-07-02 30870216+gaaclarke@users.noreply.github.com [Impeller] fixed units for memory measurement (flutter/engine#53687) 2024-07-02 jhy03261997@gmail.com [deep link][ios] Update openURL method to reflect the result from framework (flutter/engine#52643) 2024-07-02 skia-flutter-autoroll@skia.org Manual roll Dart SDK from c23e58143793 to ffc8bb004a64 (2 revisions) (flutter/engine#53690) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC aaclarke@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
fyi: I just stumbled upon an error that is related to this PR. No idea yet wether this is an issue with |
Feel free to create an issue when more details. |
I encountered similar issue with Milad-Akarie/auto_route_library#2055. Expected behaviour: Observed behaviour: <When Steps to reproduce
<When
|
@junka-0 Thank you, the information you provided is very beneficial! In flutter code
Looks like what happened might be when you click on the same universal link the second time, framework returns false for Not sure why it worked the first time clicking the universal link but it failed the second time, this might be a framework issue or a go_router issue or some issues in your project, let me investigate further. |
So I tested with a simple go_router example, and a misconfigured go_router example. The former one works well, but the latter one has the issue that it jumps to the browser again and again. If you have some misconfiguration in your app and the routing is not handled correctly and the framework returns false in function But the best way to know if the issue is on the flutter side or the app side is if you can raise a separate issue with a minimal repo that can reproduce the issue, we can see where the error is. example 1 with go_router
example 2 with go_router and it's slightly misconfigured
|
@hannah-hyj |
@junka-0 |
@hannah-hyj |
I highly recommend moving the conversation to a github issue as a closed PR isn't great for issue discussion. |
follow up on comments on #52350
framework pr : flutter/flutter#147901
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.