Deep linking
A deep link maps an external URL to an in-app destination. Done right, a
shared link opens the app on the exact screen; done wrong, it bounces to
the website or a blank home screen. The setup is fiddly and
platform-specific: association files, intent filters, and a router that
treats every incoming URL as untrusted.
Method
- Use verified links, not custom schemes, for web-originating URLs.
Configure iOS Universal Links with an
apple-app-site-association file
at the domain root (served over HTTPS, correct application-identifier,
no redirect) and Android App Links with assetlinks.json plus
autoVerify intent filters. These open the app directly and cannot be
hijacked by another app the way a myapp:// scheme can. Keep a custom
scheme only as an internal fallback.
- Route through one entry point with typed, validated parsing. Funnel
every link through a single router that parses path and query into a
typed destination and rejects anything malformed. Treat URL parameters
as hostile input: an id in a link can be forged, so authorize on arrival
rather than trusting the link to imply access.
- Build the parent back stack on arrival, do not just show the leaf.
When a link lands deep, synthesize the navigation stack beneath it so
Back reaches a real parent, not app exit. This is the same back-stack
discipline as in-app navigation, applied to an externally triggered
entry.
- Handle deferred deep links for users without the app. When a link
targets content but the app is not installed, send the user to the store,
then on first launch route to the original destination. Carry the intent
through install using a deferred-link SDK or a fingerprint/clipboard
match, and account for the privacy limits that make deferred matching
probabilistic, not guaranteed.
- Cover cold start, warm resume, and running states. A link must work
when the app is not running (parse in the launch handler), backgrounded
(
onNewIntent, continueUserActivity), and foreground. Each path is a
distinct code route and each is a distinct bug; test all three.
- Version the URL contract and keep old links working. Links live
forever in emails, messages, and search results. Never repurpose a path;
redirect retired routes to a sensible screen so a two-year-old link does
not dead-end.
Testing matrix
- App state: not installed, installed-not-running, backgrounded,
foreground. Trigger each with
adb shell am start -a android.intent.action.VIEW -d "<url>"
and xcrun simctl openurl booted "<url>".
- Source: browser address bar, Notes/Messages tap, email client, another
app. Some sources strip or wrap URLs; a link that works pasted may fail
from an email client.
- Verify the association files with the platform validators and confirm no
redirect sits in front of them.
Boundaries
This covers the URL-to-screen mapping, association setup, and deferred
install flow. The in-app hierarchy the link lands into is mobile-navigation;
routing a notification tap is push-notifications. Attribution accuracy and
campaign analytics beyond first-open routing are a marketing concern, not
part of the link contract.
1---2name: deep-linking3description: Set up universal and app links that open the right in-app screen from a URL, with deferred links surviving install and a routing layer that validates every parameter. Use when adding shareable links, wiring campaign or email links into the app, or debugging links that open the browser instead.4---56# Deep linking78A deep link maps an external URL to an in-app destination. Done right, a9shared link opens the app on the exact screen; done wrong, it bounces to10the website or a blank home screen. The setup is fiddly and11platform-specific: association files, intent filters, and a router that12treats every incoming URL as untrusted.1314## Method15161. **Use verified links, not custom schemes, for web-originating URLs.**17 Configure iOS Universal Links with an `apple-app-site-association` file18 at the domain root (served over HTTPS, correct `application-identifier`,19 no redirect) and Android App Links with `assetlinks.json` plus20 `autoVerify` intent filters. These open the app directly and cannot be21 hijacked by another app the way a `myapp://` scheme can. Keep a custom22 scheme only as an internal fallback.232. **Route through one entry point with typed, validated parsing.** Funnel24 every link through a single router that parses path and query into a25 typed destination and rejects anything malformed. Treat URL parameters26 as hostile input: an id in a link can be forged, so authorize on arrival27 rather than trusting the link to imply access.283. **Build the parent back stack on arrival, do not just show the leaf.**29 When a link lands deep, synthesize the navigation stack beneath it so30 Back reaches a real parent, not app exit. This is the same back-stack31 discipline as in-app navigation, applied to an externally triggered32 entry.334. **Handle deferred deep links for users without the app.** When a link34 targets content but the app is not installed, send the user to the store,35 then on first launch route to the original destination. Carry the intent36 through install using a deferred-link SDK or a fingerprint/clipboard37 match, and account for the privacy limits that make deferred matching38 probabilistic, not guaranteed.395. **Cover cold start, warm resume, and running states.** A link must work40 when the app is not running (parse in the launch handler), backgrounded41 (`onNewIntent`, `continueUserActivity`), and foreground. Each path is a42 distinct code route and each is a distinct bug; test all three.436. **Version the URL contract and keep old links working.** Links live44 forever in emails, messages, and search results. Never repurpose a path;45 redirect retired routes to a sensible screen so a two-year-old link does46 not dead-end.4748## Testing matrix4950- App state: not installed, installed-not-running, backgrounded,51 foreground. Trigger each with52 `adb shell am start -a android.intent.action.VIEW -d "<url>"`53 and `xcrun simctl openurl booted "<url>"`.54- Source: browser address bar, Notes/Messages tap, email client, another55 app. Some sources strip or wrap URLs; a link that works pasted may fail56 from an email client.57- Verify the association files with the platform validators and confirm no58 redirect sits in front of them.5960## Boundaries6162This covers the URL-to-screen mapping, association setup, and deferred63install flow. The in-app hierarchy the link lands into is mobile-navigation;64routing a notification tap is push-notifications. Attribution accuracy and65campaign analytics beyond first-open routing are a marketing concern, not66part of the link contract.