On iOS, the Smler React Native SDK detects deferred deep links by reading the device clipboard. When a user clicks a Smler short link before installing the app, the link is copied to the clipboard on your landing page. After the user installs and opens the app, the SDK reads the clipboard and matches it against your allowed URL patterns.
How It Works
A user clicks a deep link on the web (e.g.
https://go.yourdomain.com/abc123).Your landing page copies the full URL to the clipboard using JavaScript, then redirects the user to the App Store.
The user installs and opens your app.
On first launch, your app calls
getInstallReferrerIos()with a list of allowed URL patterns.The SDK reads the clipboard and checks if the text matches any of your patterns.
If it matches, the SDK returns the full deep link along with parsed query parameters and path information.
This approach works even under iCloud Private Relay, since it does not rely on IP-based fingerprinting or tracking cookies.
Basic Usage
import { SmlerDeferredLink } from '@smler/deferred-link';
import type { IosClipboardDeepLinkResult } from '@smler/deferred-link';
const result: IosClipboardDeepLinkResult | null =
await SmlerDeferredLink.getInstallReferrerIos({
deepLinks: [
'https://go.yourdomain.com',
'http://go.yourdomain.com',
'go.yourdomain.com',
],
});
if (result) {
console.log('Deep link found:', result.fullDeepLink);
console.log('Query params:', result.queryParameters);
console.log('Short code:', result.pathParams.shortCode);
console.log('DLT header:', result.pathParams.dltHeader);
} else {
console.log('No matching deep link found on clipboard');
}Important: This method only works on iOS. Calling it on Android will throw an error: "getInstallReferrerIos() is only intended for iOS."
IosClipboardDeepLinkResult Fields
Field | Type | Description |
|---|---|---|
|
| The full deep link string exactly as read from the clipboard |
|
| Alias for |
|
| Parsed query parameters from the URL (e.g. |
|
| Extracted |
PathParams
Field | Type | Description |
|---|---|---|
|
| The short URL code extracted from the path |
|
| Optional DLT header/campaign identifier |
URL Pattern Matching
The deepLinks parameter accepts an array of URL patterns to match against the clipboard text. The matching logic supports several flexible formats:
Supported Pattern Formats
Pattern | Matches |
|---|---|
| Exact domain (with https scheme) |
| Exact domain (with http scheme) |
| Exact domain (schemeless) |
| Domain with specific path prefix |
| Wildcard subdomain — matches |
| Domain with any path |
| Domain with any path under |
| Global wildcard — matches any clipboard text that parses as a valid URL |
Matching Rules
Scheme normalization: Both the clipboard text and the pattern are stripped of
http://andhttps://before comparison.www.stripping: Thewww.prefix is stripped from both sides. Sowww.example.commatchesexample.comand vice versa.Subdomain matching: Any subdomain of the pattern's base domain is matched.
go.example.comwill match a pattern ofexample.com.Wildcard host:
*.example.commatchesexample.comitself and any subdomain.Wildcard path: A trailing
/*will match any path after the fixed prefix.
Example With Multiple Patterns
const result = await SmlerDeferredLink.getInstallReferrerIos({
deepLinks: [
'https://go.yourdomain.com/profile',
'http://go.yourdomain.com/profile',
'go.yourdomain.com/profile',
'go.yourdomain.com', // base domain
'*.yourdomain.com/profile/*', // wildcard subdomain + path
],
});Handling the Result
The method returns null in these cases:
The clipboard is empty or contains only whitespace
The clipboard text does not match any of the provided patterns
The clipboard text cannot be parsed as a valid URL
Clipboard reading fails (e.g. permission denied)
When a match is found, the full URL is parsed and you receive:
The complete deep link string
All query parameters as a key-value object
The
shortCodeanddltHeaderextracted from the URL path
After Matching: Resolving the Link
Once you have the deep link from the clipboard, resolve it to get the original URL and full metadata:
if (result) {
const data = await SmlerDeferredLink.resolveDeepLink(
result.fullReferralDeepLinkPath,
{ triggerWebhook: true }
);
console.log('Original URL:', data.originalUrl);
console.log('Short code:', data.shortCode);
console.log('Domain:', data.domain);
// Navigate to the correct screen based on data.originalUrl
}Falling Back to Probabilistic Matching
If getInstallReferrerIos() returns null (no matching clipboard link), you can fall back to probabilistic matching:
import { SmlerDeferredLink, HelperReferrer } from '@smler/deferred-link';
const result = await SmlerDeferredLink.getInstallReferrerIos({
deepLinks: ['go.yourdomain.com'],
});
if (result) {
// Clipboard match found — resolve and navigate
const data = await SmlerDeferredLink.resolveDeepLink(result.fullDeepLink, {
triggerWebhook: true,
});
// Navigate based on data
} else {
// No clipboard match — try probabilistic matching
const match = await HelperReferrer.getProbabilisticMatch({
domain: 'go.yourdomain.com',
});
if (match.matched && (match.score ?? 0) > 0.65) {
console.log('Probabilistic match:', match.pathParams?.shortCode);
// Navigate based on match.pathParams
}
}See the Probabilistic Matching guide for full details on the fallback.
Full iOS Example
import { Platform } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { SmlerDeferredLink, HelperReferrer } from '@smler/deferred-link';
const DOMAIN = 'go.yourdomain.com';
async function handleIosDeferredAttribution() {
if (Platform.OS !== 'ios') return;
// Only run on first install
const hasRun = await AsyncStorage.getItem('smler_first_install');
if (hasRun !== null) return;
try {
// 1. Check clipboard for a matching deep link
const result = await SmlerDeferredLink.getInstallReferrerIos({
deepLinks: [
`https://${DOMAIN}/profile`,
`http://${DOMAIN}/profile`,
`${DOMAIN}/profile`,
DOMAIN,
],
});
if (result) {
// 2. Clipboard match found — resolve it
console.log('Clipboard deep link:', result.fullDeepLink);
const data = await SmlerDeferredLink.resolveDeepLink(
result.fullReferralDeepLinkPath,
{ triggerWebhook: true }
);
// Navigate to screen based on data.originalUrl
} else {
// 3. Fallback to probabilistic matching
console.log('No clipboard match — trying probabilistic...');
const match = await HelperReferrer.getProbabilisticMatch({
domain: DOMAIN,
});
if (match.matched && (match.score ?? 0) > 0.65) {
// Navigate based on match.pathParams
}
}
} catch (error) {
console.error('iOS deferred attribution failed:', error);
}
await AsyncStorage.setItem('smler_first_install', 'done');
}
Published with LeafPad