-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
handleUniversalLink always returns false when app launch after installation #6978
Comments
I found a few problems with this issue:
|
Internal bug number 174061690. |
same here, always getting false |
Anything new in this? :( |
Any update with this? |
For the open after first install case, we should call 'DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url)', right? func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { |
I am not sure this is the correct place to put it, but I found the following piece of code in the FDLUtilities.m in the iOS FirebaseDynamicLinks library (version 8.9.0 as we speak): `BOOL FIRDLIsURLForAllowedCustomDomain(NSURL *_Nullable URL) {
} This piece of code is called when using the The thing I am concerned about is this line: This is plain wrong! We are using custom domains in our dynamic links for iOS. However, a long custom domain dynamic link will only be recognised as such when the In our app, we are also using the The fix here seems easy enough.... Update the FIRDLIsURLForAllowedCustomDomain to e.g. use URLComponents to search for the existence of the link query parameter instead of a regex... |
@eldhosembabu PTAL ^ |
Just came across this too and saw the same thing. We have a dynamic link that is not being recognized as such and step debugging I too came across this regex in the Firebase pod which only matches when link query param is first argument, because the regex matches |
Hi @ctsrc , @brunsman I've tried adding couple of test cases where the link query param is not the first argument. See here : But in those cases, I could see the test is passing in local. Do you mind sharing some sample urls which you were having issues with so that I can test locally and check where the issue is? |
I created a minimal repro of the problem. Can put the Xcode project for the repro on GitHub as well if you like, but here is the essence: Minimal <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>FirebaseDynamicLinksCustomDomains</key>
<array>
<string>https://repro-1648070683.ifee.fr</string>
</array>
</dict>
</plist> We use Firebase func createExampleDynamicLink() -> URL? {
guard let link = URL(string: "https://ifee.fr/musique-et-technologie/") else { return nil }
let dynamicLinksDomainURIPrefix = "https://repro-1648070683.ifee.fr"
let linkBuilder = DynamicLinkComponents(link: link, domainURIPrefix: dynamicLinksDomainURIPrefix)
linkBuilder?.iOSParameters = DynamicLinkIOSParameters(bundleID: Bundle.main.bundleIdentifier ?? "")
linkBuilder?.iOSParameters?.appStoreID = "1648070683"
linkBuilder?.androidParameters = DynamicLinkAndroidParameters(packageName: "com.example.repro-1648070683")
guard let longDynamicLink = linkBuilder?.url else { return nil }
return longDynamicLink
} URLs returned by link builder look like: https://repro-1648070683.ifee.fr/?ibi=fr%2Eifee%2Erepro%2D1648070683&isi=1648070683&apn=com%2Eexample%2Erepro%2D1648070683&link=https%3A%2F%2Fifee%2Efr%2Fmusique%2Det%2Dtechnologie%2F But when we pass such a URL and a completion handler to let longDynamicLink1 = createExampleDynamicLink()!
print("longDynamicLink1 is: \(longDynamicLink1)")
DynamicLinks.dynamicLinks().handleUniversalLink(longDynamicLink1) { (link, error) in
guard error == nil else { return }
print("The link for longDynamicLink1 is: \(String(describing: link?.url))")
} Then You can see where this happen with a breakpoint at line 221 in this file from the Firebase Dynamic Links library: firebase-ios-sdk/FirebaseDynamicLinks/Sources/Utilities/FDLUtilities.m Lines 218 to 225 in 6efe4f5
Whereas if we take the long dynamic link from above that it was unable to extract link from: and we manually rearrange it so that the Then let longDynamicLink2 = URL(string: "https://repro-1648070683.ifee.fr/?link=https%3A%2F%2Fifee%2Efr%2Fmusique%2Det%2Dtechnologie%2F&ibi=fr%2Eifee%2Erepro%2D1648070683&isi=1648070683&apn=com%2Eexample%2Erepro%2D1648070683")!
print("longDynamicLink2 is: \(longDynamicLink2)")
DynamicLinks.dynamicLinks().handleUniversalLink(longDynamicLink2) { (link, error) in
guard error == nil else { return }
print("The link for longDynamicLink2 is: \(String(describing: link?.url))")
} The result being that |
@eldhosembabu : Commenting on why your tests succeed.
The link parameter in your example is NOT urlEncoded. In our examples the link parameter IS urlencoded.
take a look at the regex:
the test WILL fail. Perhaps it's an idea to replace all query parameter links in those tests with urlencoded links. |
Thanks @brunsman and @ctsrc for explaining the issue. The issue was not because of URL encoding, its related with the link param ordering only. I've modified the logic here to handle these cases : #9516 Would be great if you can clone this PR and check whether its working for you and let me know so that I can push in the changes for next release. |
@eldhosembabu I think what @brunsman meant is that when the link is URL encoded as in his case and in my case, the regex does not accidentally match the slashes in |
Will do. Going to report back when I've done so. |
Unfortunately have to say that the fix needs some improvements. The following link e.g. will count as valid while it is not: BOOL FIRDLIsURLForAllowedCustomDomain(NSURL *_Nullable URL) {
BOOL customDomainMatchFound = false;
for (NSURL *allowedCustomDomain in FIRDLCustomDomains) {
// At least one custom domain host name should match at a minimum.
if ([allowedCustomDomain.host isEqualToString:URL.host]) {
NSString *urlStr = URL.absoluteString;
NSString *domainURIPrefixStr = allowedCustomDomain.absoluteString;
// make sure the paths match since we no longer use the regex.
// We want to avoid that a path with a longer name is considered valid when it is a completely different path
// "https://a.firebase.com/mypath" <- FIRDLCustomDomain
// "https://a.firebase.com/mypaths" <- URL. Will be accepted without this check
BOOL pathPrefixMatch = allowedCustomDomain.pathComponents && URL.pathComponents && allowedCustomDomain.pathComponents.count <= URL.pathComponents.count && [allowedCustomDomain.pathComponents isEqualToArray:[URL.pathComponents subarrayWithRange:NSMakeRange(0, allowedCustomDomain.pathComponents.count)]];
// Next, do a string compare to check if entire domainURIPrefix matches as well.
// NB: left this one since it also compares e.g. the port if present
if (([urlStr rangeOfString:domainURIPrefixStr
options:NSCaseInsensitiveSearch | NSAnchoredSearch]
.location) == 0 && pathPrefixMatch) {
NSString *urlWithoutDomainURIPrefix = [urlStr substringFromIndex:domainURIPrefixStr.length];
// For a valid custom domain DL Suffix:
// 1. At least one path exists OR
// 2. Should have a link query param with an http/https link
NSURLComponents *components = [[NSURLComponents alloc] initWithString:urlWithoutDomainURIPrefix];
BOOL hasPath = components.path && components.path.length > 1;
BOOL hasLinkQueryParameter = false;
if (components.queryItems && components.queryItems.count > 0) {
for (NSURLQueryItem *queryItem in components.queryItems) {
hasLinkQueryParameter = hasLinkQueryParameter || [queryItem.name caseInsensitiveCompare:@"link"] == NSOrderedSame;
}
}
BOOL matchesRegularExpression = hasPath || hasLinkQueryParameter;
if (matchesRegularExpression) {
customDomainMatchFound = true;
break;
}
}
}
}
return customDomainMatchFound;
} |
Thanks for your time to test the change and find the test case which could fail. I've added logic to filter the same in PR : #9516. Please take a look and let me know if you can see other cases. |
More test cases. The top one has a different path than the allowed one (includes an s). The other links contain the link parameter in the fragment. |
hi @brunsman, Thanks again finding these corner cases. It seems like these bugs were from long back. I've did a refactoring on the custom domain validation logic and have added the test cases that you have provided here : #9516. Please let me know whether you can find any issues on this refactored code. Thanks |
Removed the usage of regex as there is a chance we might miss if the link param comes under other url which comes as a query param of actual url. So to make the code easy to understand and to avoid missing of those corner cases, i've refactored the code again to use native code to validate instead of regex: #9516. |
This looks a lot better! Great refactor. |
Closing this issue since the changes are merged and will be out in next release. |
Step 1: Describe your environment
Step 2: Describe the problem
handleUniversalLink
always returnsfalse
when app launch after installationSteps to reproduce:
Save my place in the app. A link will be copied to continue to this page.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:])
called with url from firebaseExpected behavior:
DynamicLinks.dynamicLinks().handleUniversalLink(url)
returns trueReal behavior:
DynamicLinks.dynamicLinks().handleUniversalLink(url)
always returns falseWhen Im opening installed app from this link all works as expected.
Diagnostic output:
The text was updated successfully, but these errors were encountered: