Skip to main content

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:

PrioritymatchTypeConfidenceWhere
1CLIPBOARDexactBoth platforms; the redirect page's copy affordance writes a one-time token
2INSTALL_REFERRERexactAndroid only; the token rides the Play Store referrer
3FINGERPRINT_EXACThighBoth; all collected device attributes match exactly
4FINGERPRINT_SCOREDhigh / medium / lowBoth; weighted scoring across attributes
5IP_FUZZYlowBoth; 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​

PlatformPromptMeaning
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 < 16One-time non-blocking paste bannerSame 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 the OptoLinkData?.
  • No match and failure both return null; OptoLink.lastError is null for 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.