Skip to main content

Test your integration

One link, one device, seven checks. This walks every delivery path end to end and shows the diagnostics you need when a step misbehaves. Create a test link in the OptoLink portal first (path /promo/flash-24h works); its canonical URL is https://links.yourdomain.com/acme/shortCode.

The plugin repo ships an example/ harness (auto-init, live links log, Resolve deferred and Reset install state buttons) you can run against your org for the same checks.

Printing a payload with debugPrint(data) gives you the assertions below:

OptoLinkData(path: /promo/flash-24h, matchType: DIRECT, confidence: MatchConfidence.exact, score: null, isDeferred: false)

1. Initialize and device ID​

Run the app.

Expected: a non-null UUID deviceId, and INFO lines prefixed OptoLink [info] in the console (debug builds print the trail without a logger). A thrown OptoLinkError here means a config value the native side rejected (most often a malformed baseUrl) or a missing native plugin.

2. Direct open, app installed (warm)​

Leave the app running or backgrounded, then tap the short URL from Messages or Notes (iOS) / Chrome (Android).

Expected: the app opens and onLink receives exactly one event: matchType: DIRECT, confidence: exact, isDeferred: false. linkId is set when the short code resolves server-side (it stays null if the backend was unreachable at resolve time).

3. Direct open, app closed (cold)​

Force-quit the app, then tap the short URL again.

Expected: the app cold-starts, getInitialLink() returns the same DIRECT payload, and onLink fires zero events. If a DIRECT event reaches onLink on a cold start, you are testing a warm open by accident or running 1.x behavior.

4. Deferred match, iOS​

  1. Uninstall the app (clears the once-per-install match marker).
  2. In Safari, open the short URL. The redirect page shows a store button; tapping it writes the attribution token to the clipboard before navigating.
  3. Install and launch the app.
  4. First launch shows the paste-permission dialog. Tap Allow; that is the expected flow (see deferred deep linking).
  5. onLink receives matchType: CLIPBOARD, confidence: exact, isDeferred: true.

Simulator shortcut: the App Store is not installable there, so prime the pasteboard directly with the URL-plus-token payload the redirect page writes. A bare token does not pass the SDK's pasteboard gate:

xcrun simctl pbcopy booted "https://links.yourdomain.com/acme/shortCode?opl_click=YOUR_TOKEN"

5. Deferred match, Android​

  • Play install (primary). On a device without the app, tap the short URL in Chrome, then Get it on Google Play, then install from the listing. First launch: onLink receives matchType: INSTALL_REFERRER, confidence: exact, isDeferred: true. Requires a real Play-managed install; emulators without the Play Store cannot exercise this leg.
  • Clipboard fallback. Tap the Open button on the redirect page before installing (the tap is what writes the clipboard), then sideload or install. Expected: matchType: CLIPBOARD, confidence: exact, isDeferred: true.

6. Identity and events​

await optoLink.setIdentity('usr_88213'); // true
await optoLink.trackEvent('test_event', properties: {'k': 'v'}); // true
final ok = await optoLink.trackEvent(
'oversize', properties: {'blob': 'x' * 11264});
// false: properties exceed the 10 KiB serialized cap;
// OptoLink.lastError.status == 400

7. No-match sanity​

Fresh install with no prior click: resolveDeferredLink() returns null and OptoLink.lastError is also null — a genuine no-match is not a failure. A null with lastError set is a failed call.

Common verification mistakes​

  • Cached install state. The deferred match runs once per install; reusing an install silently shows nothing. Uninstall first, or use the example harness's Reset install state (kill and relaunch after).
  • Entitlement changes without a rebuild. iOS entitlements ride the signed binary and hot reload does not propagate them. flutter clean && flutter run.
  • Taps from inside Safari. A tap on a link rendered in a Safari page loads the redirect page (Apple's same-origin policy), not your app. Test from Messages, Notes, or the address bar. Expected, not a bug.
  • Staged fingerprint matches. You cannot stage the probabilistic legs by hand; they fire only when clipboard and referrer are absent (for example, an App Store bounce before the copy). If a scored match shows up in your logs, data.score tells you its tier.

When a step fails​

  1. Read OptoLink.lastError after the failed call: status, message, requestId, isConnectionError.
  2. Read the INFO trail. It covers the init summary, the per-rung deferred decisions (what each leg read), per-attempt request lines, and the final link emission. Debug builds print it; wire a logger to ship it to your crash reporter.
  3. Still stuck: troubleshooting.