-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[deep link][ios] Update openURL method to reflect the result from framework #52643
base: main
Are you sure you want to change the base?
Changes from 17 commits
c762720
b8d8083
bbbb148
50dbd75
4fec71f
6af454a
9b28f1e
1dcf138
ca5e4d8
7d00b0e
62b38db
2c38727
b63243b
308fdac
17949bf
4ec56ec
77c56fe
7fa8d0c
2befe86
8c595b7
785fbc8
1a6703e
c9a38bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -134,33 +134,25 @@ - (void)userNotificationCenter:(UNUserNotificationCenter*)center | |
} | ||
} | ||
|
||
- (BOOL)openURL:(NSURL*)url { | ||
- (BOOL)isFlutterDeepLinkingEnabled { | ||
NSNumber* isDeepLinkingEnabled = | ||
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"FlutterDeepLinkingEnabled"]; | ||
if (!isDeepLinkingEnabled.boolValue) { | ||
// Not set or NO. | ||
return NO; | ||
// if not set, return NO | ||
return isDeepLinkingEnabled ? [isDeepLinkingEnabled boolValue] : NO; | ||
} | ||
|
||
- (void)openURL:(NSURL*)url | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. follow up from meeting - can delete this. |
||
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 commentThe 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 commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Yes they would set this to no. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
completion(NO); | ||
} else { | ||
FlutterViewController* flutterViewController = [self rootFlutterViewController]; | ||
if (flutterViewController) { | ||
[flutterViewController.engine | ||
waitForFirstFrame:3.0 | ||
callback:^(BOOL didTimeout) { | ||
if (didTimeout) { | ||
FML_LOG(ERROR) | ||
<< "Timeout waiting for the first frame when launching an URL."; | ||
} else { | ||
[flutterViewController.engine.navigationChannel | ||
invokeMethod:@"pushRouteInformation" | ||
arguments:@{ | ||
@"location" : url.absoluteString ?: [NSNull null], | ||
}]; | ||
} | ||
}]; | ||
return YES; | ||
[flutterViewController openDeepLink:url completionHandler:completion]; | ||
} else { | ||
FML_LOG(ERROR) << "Attempting to open an URL without a Flutter RootViewController."; | ||
return NO; | ||
completion(NO); | ||
} | ||
} | ||
} | ||
|
@@ -171,7 +163,20 @@ - (BOOL)application:(UIApplication*)application | |
if ([_lifeCycleDelegate application:application openURL:url options:options]) { | ||
return YES; | ||
} | ||
return [self openURL:url]; | ||
|
||
__block BOOL openURLSuccess = NO; // Flag to track success | ||
hangyujin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Use a dispatch semaphore to wait for the completion handler | ||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You won't want a dispatch_semaphore on the main thread, that will cause hangs and potentially deadlocks. The run loop is a processing loop for event handling, and there's one assigned to the main thread. I believe what you want is to kick off the request, and spin the main run loop until it completes, then return the value from this method. I didn't run this code so you should test it, but something like: __block BOOL openURLSuccess = NO;
__block BOOL openURLCompleted = NO;
[self openURL:url
options:options
completionHandler:^(BOOL success) {
openURLSuccess = success;
openURLCompleted = YES;
}];
while (!openURLCompleted) {
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
return openURLSuccess; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for educating me about the The run loop I have some more questions :
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Are you concerned the code you're calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm also a bit concerned " navigationChannel invokeMethod:arguments:result: " will time out 😅 Is it possible? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could add a timeout instead of the while loop, that would work too. And return NO if it times out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just wanna update my progress so far. So for the deadlock, I wasn't able to find anything useful online, but I did come up with a theory - the If my theory is correct, I don't think there's a way to synchronize the async API. I am thinking about a workaround (which I am still hashing out). Basically we want to return
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. UIAppilcation::openURL which should throw the url back to iOS >> what will happen then? will it open the url in the browser? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hangyujin My understanding is that, it should open in the next best candidate app that registers the domain, and browser is the catch-all default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am still playing around with the deep link API, but from my personal experience with Amazon: Whenever i get an amazon package, I receive a link through text that starts with This makes me think that Amazon app probably returned YES in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think throwing back it to iOS is a reasonable idea. I updated the code to add [UIApplication.sharedApplication openURL: url]. Also tested that custom URI scheme is using a different API application:openURL:options I also tried return NO in application:continueUserActivity:restorationHandler for (universal link)and application:openURL:options (for custom URI), return NO will not cause it to throw the URL back, the behavior looks the same as return YES. So I don't know how the return value here is used. Also |
||
|
||
[self openURL:url | ||
options:options | ||
completionHandler:^(BOOL success) { | ||
openURLSuccess = success; | ||
dispatch_semaphore_signal(semaphore); | ||
}]; | ||
|
||
dispatch_semaphore_wait(semaphore, 5 * NSEC_PER_SEC); | ||
return openURLSuccess; | ||
} | ||
|
||
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url { | ||
|
@@ -214,7 +219,20 @@ - (BOOL)application:(UIApplication*)application | |
restorationHandler:restorationHandler]) { | ||
return YES; | ||
} | ||
return [self openURL:userActivity.webpageURL]; | ||
|
||
__block BOOL openURLSuccess = NO; // Flag to track success | ||
// Use a dispatch semaphore to wait for the completion handler | ||
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); | ||
|
||
[self openURL:userActivity.webpageURL | ||
options:@{} | ||
completionHandler:^(BOOL success) { | ||
openURLSuccess = success; | ||
dispatch_semaphore_signal(semaphore); | ||
}]; | ||
|
||
dispatch_semaphore_wait(semaphore, 5 * NSEC_PER_SEC); | ||
return openURLSuccess; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can you write a helper to avoid duplicate code? |
||
} | ||
|
||
#pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController | ||
|
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 returnNO
.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