-
Notifications
You must be signed in to change notification settings - Fork 83
[SDK-45] Fix iOS deep link resolution failures for greenFi #974
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
base: master
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #974 +/- ##
===========================================
- Coverage 85.19% 69.39% -15.81%
===========================================
Files 91 109 +18
Lines 6301 8929 +2628
===========================================
+ Hits 5368 6196 +828
- Misses 933 2733 +1800 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| completionHandler(nil) | ||
| return | ||
| } | ||
|
|
||
| guard let url = response.url else { | ||
| delegate?.onRedirect(deepLinkLocation: deepLinkLocation, campaignId: campaignId, templateId: templateId, messageId: messageId) | ||
| completionHandler(nil) |
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.
Why do we need the nil completionHandler 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.
thats just a bug that was there the completionhandler wasnt fired if there was no info in the headers
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 see! So, to double check, what we want is (as per the documentation) to pass nil on the completionHandler so we let "the body of the redirection response to be delivered as the payload of this request" correct?
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.
basically completion handler should be always fired before returning 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.
we pass nil on the completionHandler to not follow any further redirects the redirection is passed in the delegate callbacks
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 effectively yes when the conpletionHandler(nil) is fired it would finish the redirection flow
|
@sumeruchat Is this issue happening specifically with shortened urls with SMS only or with any re-written url? |
|
@davidtruong It would happen a /a url in particular that would follow the redirect but if iOS returned an error of any kind it wouldnt use the redirect url |
Do we know why a NSURLErrorCancelled would get called for the original request? |
| // Check if we successfully captured redirect data FIRST | ||
| // The delegate callback happens before the error, so deepLinkLocation | ||
| // may be set even if we get NSURLErrorTimedOut (-1001) or | ||
| // NSURLErrorCancelled (-999) from cancelling the redirect | ||
| if self.deepLinkLocation != nil { |
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 understand that the deepLinkLocation is updated on the onRedirect delegate method. It's just weird that we are conditioning the logic of this makeDataRequest completionHandler be dependent on something happening elsewhere.
Logically I think this makes sense, but is there a way for us to better document this in code or use a different architecture to ensure it makes sense to disregard an error on a networkRequest 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.
well i wouldnt recommend refactoring but logically it means that we have a redirect url we use it no matter what the http connection or tcp connection returns. Its more robust than before
|
|
|
@davidtruong So basically I was thinking that passing nil to the completionHandler in willPerformHTTPRedirection might return a cancelled error or its also possible that the request times out due to the server being slow to responr or bad network and it doesnt use the URL it already has received. |
|
@davidtruong Another bug we fixed is that if we dont call the completionHandler at all before return in the guard condition it might hang and not complete the request at all. |
Ticket: https://iterable.atlassian.net/browse/MOB-11490
Issue
GreenFi, 2ULaundry, and Hello Heart reported that iOS deep links were failing intermittently. Users would tap SMS/email links but the app wouldn't open to the correct screen. This was blocking all iOS deep linking for these clients.
What Was Happening
When a user taps an Iterable deep link (like
https://links.greenfi.com/a/N2Nbu), the SDK needs to:The problem was in step 3. Here's what was going wrong:
The SDK intercepts the redirect to capture attribution cookies, then calls
completionHandler(nil)to prevent actually following the redirect (we only want one hop). However, when the redirect is cancelled, URLSession sometimes returns an error - eitherNSURLErrorTimedOut(-1001) orNSURLErrorCancelled(-999).The original code checked for errors FIRST:
But by this point, the redirect delegate had ALREADY successfully captured the destination URL! We were throwing away perfectly good data just because URLSession reported an error from cancelling the redirect.
Why It Was Intermittent
This was a race condition based on network timing:
So ironically, clients with better network conditions were MORE likely to experience failures.
The Fix
We reversed the logic to check if we captured the redirect URL FIRST, before checking for errors:
We also fixed two missing
completionHandler(nil)calls in early return paths that could cause URLSession tasks to hang.Why This Works
The redirect delegate is called SYNCHRONOUSLY when the 303 response arrives, so
deepLinkLocationis always set before the completion handler runs. By checking for this data first, we use it regardless of any subsequent timeout/cancellation errors from URLSession.This matches what we saw in 2ULaundry's successful cases - they were getting the location and opening links correctly, just without attribution data (separate cookie parsing issue).
Impact
/a/*URL patterns (all Iterable email/SMS links)