Deferred deep linking
A deferred link carries link intent across the install flow: someone taps your short URL without the app installed, installs it, and the first launch resolves the original link. The native SDK runs the match once per install, automatically, as part of initialize. Your code reads the result off onLink like any other link.
The match ladder
The SDK collects every signal it can and sends one match request; the backend decides which signal wins:
| Priority | matchType | Confidence | Where |
|---|---|---|---|
| 1 | CLIPBOARD | exact | Both platforms; the redirect page's copy affordance writes a one-time token |
| 2 | INSTALL_REFERRER | exact | Android only; the token rides the Play Store referrer |
| 3 | FINGERPRINT_EXACT | high | Both; all collected device attributes match exactly |
| 4 | FINGERPRINT_SCORED | high / medium / low | Both; weighted scoring across attributes |
| 5 | IP_FUZZY | low | Both; same IP only, last resort |
iOS has no install-referrer channel, so its ladder is clipboard → fingerprint → IP. A deterministic match (CLIPBOARD, INSTALL_REFERRER) always wins; the probabilistic legs fire only when the earlier signals are missing.
Scored matches also carry OptoLinkData.score (0–100); every other match type has score == null. Expect a platform asymmetry on the scored leg: the Android collector sends timezone and Chrome client hints, while the iOS match body deliberately omits timezone, so iOS scores land a tier lower. In live testing through the plugin, Android scored matches landed at 86 (high) and iOS at 79 (medium).
What the user sees on first launch
| Platform | Prompt | Meaning |
|---|---|---|
| iOS 16+ | Paste permission dialog ("Allow … to paste from …?") | The clipboard leg running. Tapping Allow lets the match use the token; denying skips the leg and falls through to fingerprint/IP. |
| iOS < 16 | One-time non-blocking paste banner | Same leg, notification instead of a dialog. |
| Android 12+ | System toast "… pasted from your clipboard" | Same leg. The toast is expected output, not a bug. |
Set clipboardEnabled: false in OptoLinkConfig to skip the clipboard leg entirely; matching then relies on the remaining legs.
Reading the result
The matched link streams on onLink with isDeferred: true. Deferred matches always stream and replay to the first subscriber, so subscribing right after initialize is safe:
optoLink.onLink.listen((data) {
debugPrint('${data.matchType} ${data.confidence.name} '
'score=${data.score} deferred=${data.isDeferred}');
if (data.confidence == MatchConfidence.low) {
// IP-only match: confirm with the user before navigating.
return;
}
Navigator.of(context).pushNamed(data.path, arguments: data.params);
});
Once per install, retry, and no match
- A native marker fires the match on first launch only; later launches never re-run it. Uninstalling the app re-arms it.
- If the first launch had no network, the opportunity is not lost: call
resolveDeferredLink()later. It re-runs the match and returns theOptoLinkData?. - No match and failure both return
null;OptoLink.lastErrorisnullfor a genuine no-match and set on failure, which is how you tell them apart. - Retry pacing is the natives' job (
maxRetries, default 2, exponential backoff). The Dart edge never loops.
To verify the whole flow on a device: test your integration.