Quickstart
One link, two journeys: tap it with the app installed (direct Universal Link), then install the app from a tap (deferred match). This picks up after install & initialize; the app config, associated-domains entitlement, and SDK initialization are in place.
Create the link
In the OptoLink portal, create a link with the path /promo/flash-24h. Its canonical URL is https://links.yourdomain.com/acme/<shortCode>; share that URL to a device.
Direct open (app installed)
With the app already installed, tap the link in Safari. iOS verifies the associated-domains entitlement, opens your app, and hands it the URL through one of the ingestion surfaces you wired. Collect links:
Task {
for await link in OptoLink.instance().links {
print("\(link.path) \(link.matchMethod.rawValue) deferred=\(link.isDeferred)")
}
}
The tap prints:
/promo/flash-24h DIRECT deferred=false
Behind the scenes the SDK resolved the short code via the backend and emitted an OptoLinkData with linkId, path, params, and matchMethod = .direct. handle(url:) is safe to call before initialize finishes; an early URL parks and is processed once initialization completes.
Deferred match (install from a tap)
On a device without the app:
- Tap the link in Safari. The OptoLink redirect page opens and, in the same tap, copies the link URL with a one-time attribution token (
?opl_click=…) to the clipboard, then shows the Download on the App Store button. - Install and open the app.
- On first launch,
initializedetects the URL on the pasteboard. iOS shows the system paste dialog — tap Allow — and the SDK matches the token server-side, emitting the link onlinks:
/promo/flash-24h CLIPBOARD deferred=true
The paste dialog (tap Allow) is the expected output of this test: it confirms the deferred match read the clipboard. Installs that didn't come from a tap never see it; the SDK probes the pasteboard silently first.
The match runs once per install. If the first launch had no network, the opportunity isn't lost: call resolveDeferredLink() later to re-run the match manually; it returns an OptoLinkData? (nil = no match) and throws on network failure.
What you have
Both journeys end in the same place: an OptoLinkData on the links stream, with matchMethod telling you how it arrived. From here:
- Every method and shape: API reference
- What throws, what retries: Errors & retries
- Bind the device to a user or report events with
setIdentity/trackEvent— same reference.