Learn how to use deferred deep linking to track conversions and traffic.
Deferred deep linking allows you to track which link a user came from even when they don't have your app installed.
When a user clicks a link without the app installed, they're redirected to the app store. After installing and opening the app, you can retrieve the original link information and redirect them to the appropriate screen.
Android Play Store
Android provides the Install Referrer API which allows you to retrieve information about how a user came to install your app, including the referrer URL.
Here is how it works in a nutshell:
User taps a deep link on a device without your app installed
Smler redirects the user to the App Store or Play Store by using device targeting
User installs your app from the app store
App reads the install referrer on first launch
App extracts the deep link from the referrer URL and tracks the deep link open when the app hits smler endpoint
Redirect the user to the appropriate screen using the destination URL returned by the
/api/v1/shortendpoint
Understanding the Referrer URL Structure
When Smler redirects users to the Play Store, the referrer URL contains the deep link information in a nested structure. The referrer URL looks like this:
https://play.google.com/store/apps/details?id=com.example.app&referrer=deepLink%3Dhttps%253A%252F%252Fsmler.in%252FgpsTo extract the original deep link, you need to:
Parse the
referrerparameter from the Play Store URLURL decode it to get the
deepLinkparameterURL decode the
deepLinkvalue to get the actual deep link
The code examples below show how to implement this extraction process.
Step 1: Add the Install Referrer dependency
First, you'll need to add the Google Play Install Referrer library to your project.
For react native based project add the following dependency
// package.json for react native
{
"dependencies": {
"react-native-play-install-referrer": "latest"
}
} Step 2: Implement the Install Referrer logic
Now you'll need to implement the logic to read the install referrer, extract the deep link, and track the deep link open.
// InstallReferrerTracker.js
import { PlayInstallReferrer } from "react-native-play-install-referrer";
class InstallReferrerTracker {
constructor() {
this.isFirstLaunch = true;
}
trackInstallReferrer() {
// Check if this is the first launch
if (!this.isFirstLaunch) {
return;
}
PlayInstallReferrer.getInstallReferrerInfo((installReferrerInfo, error) => {
if (!error) {
console.log(
"Install referrer = " + installReferrerInfo.installReferrer
);
if (installReferrerInfo.installReferrer) {
// Extract the deep link from the referrer URL
const deepLink = this.extractDeepLinkFromReferrer(
installReferrerInfo.installReferrer
);
if (deepLink) {
// Track the deep link open with the extracted URL
this.trackDeepLinkOpen(deepLink);
}
}
} else {
console.log("Failed to get install referrer info!");
console.log("Response code: " + error.responseCode);
console.log("Message: " + error.message);
}
this.isFirstLaunch = false;
});
}
extractDeepLinkFromReferrer(referrerUrl) {
try {
// Parse the referrer URL to extract the deep link
// e.g. for referrer=deepLink%3Dhttps%253A%252F%252Fsmler.in%252Fgps?clickId=1234343
// the deep link is https://smler.in/gps
const referrerUrlObj = new URL(referrerUrl);
const deepLinkParam = referrerUrlObj.searchParams.get("deepLink");
return decodeURIComponent(deepLinkParam);
return null;
} catch (error) {
console.error("Error extracting deep link from referrer:", error);
return null;
}
}
async trackDeepLinkOpen(deepLink) {
try {
const response = await fetch("https://smler.in/api/v1/short/track/:clickId", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
deepLink,
}),
});
if (response.ok) {
const data = await response.json();
const destinationUrl = data.link.url;
// Navigate to the destination URL in your app
this.navigateToDestination(destinationUrl);
}
} catch (error) {
console.error("Error tracking deep link open:", error);
}
}
navigateToDestination(destinationUrl) {
// Implement your navigation logic here
// This will depend on your navigation library (React Navigation, etc.)
console.log("Navigating to:", destinationUrl);
}
}
export default InstallReferrerTracker;Step 3: Initialize the tracker in your app
Now you need to initialize the Install Referrer tracker when your app starts.
// App.js
import React, { useEffect } from 'react';
import InstallReferrerTracker from './InstallReferrerTracker';
const App = () => {
useEffect(() => {
const tracker = new InstallReferrerTracker();
// Track install referrer on app launch
tracker.trackInstallReferrer();
}, []);
return (
// Your app components
);
};
export default App;Step 4: Handle the navigation
Finally, implement the navigation logic to redirect users to the appropriate screen based on the destination URL.
// InstallReferrerTracker.js (navigation method)
navigateToDestination(destinationUrl) {
// Parse the destination URL to determine which screen to navigate to
const url = new URL(destinationUrl);
const path = url.pathname;
// Example navigation logic
if (path.includes('/product/')) {
const productId = path.split('/product/')[1];
// Navigate to product detail screen
navigation.navigate('ProductDetail', { productId });
} else if (path.includes('/category/')) {
const categoryId = path.split('/category/')[1];
// Navigate to category screen
navigation.navigate('Category', { categoryId });
} else {
// Navigate to home screen
navigation.navigate('Home');
}
}Important notes
First Launch Detection: The install referrer is only available on the first launch after installation. Make sure to track this properly to avoid duplicate tracking.
URL Decoding: The referrer URL is URL-encoded multiple times. Make sure to properly decode it to extract the original deep link.
Network Operations: Ensure that all API calls are made in background threads to avoid blocking the main thread and ensure smooth app performance.
Error Handling: Always implement proper error handling for network requests and URL parsing.
Testing: Test your implementation thoroughly using the Google Play Console's internal testing track.
To get started, we recommend using the quickstart guide to set up your deep links on Smler.
Related resources
iOS App Store
Unlike Android, iOS doesn't provide a built-in install referrer API. Smler implements a hybrid approach combining deterministic tracking (clipboard-based) and probabilistic tracking (IP-based) to ensure reliable deferred deep linking on iOS.
How iOS deferred deep linking works on Smler
Smler's iOS deferred deep linking solution provides two tracking approaches:
Deterministic clipboard-based tracking: Uses iOS clipboard data to reliably identify and open the exact deep link the user interacted with.
Probabilistic Tracking: Coming soon
For the deterministic approach, when an iOS user who doesn't have your app installed clicks on your deep link, Smler redirects them to a custom landing page:
This page displays two options:
Get the App: Copies the deep link to the clipboard before redirecting to the App Store.
Get the App without Copying: Redirects directly to the App Store without copying the deep link to the clipboard.
After the user installs your app, the deep link resolution method depends on the button they clicked.
1. Deterministic clipboard-based tracking
This method uses the iOS clipboard to pass the deep link into your app after installation, ensuring reliable and direct link resolution.
Here is how it works in a nutshell:
User taps Get the App button on the landing page.
Smler copies the deep link URL to the clipboard.
The user is redirected to the App Store to download your app.
After installation, your app reads the clipboard to retrieve the deep link.
Your app calls the get long url endpoint
Smler returns the destination URL, and your app navigates the user to the appropriate screen (you need to handle this part in code).
2. Probabilistic IP-based tracking
This method relies on matching the user’s device IP address from the initial click event to the app open event after installation.
Steps will be updated soon
Track the deep link open
The following logic determines whether to use clipboard-based tracking or IP-based tracking:
Clipboard-based tracking: Used when a deep link is found in the clipboard (e.g.,
acme.link). The app sends thedeepLinkparameter in the request body.IP-based tracking: Used when no deep link is found in the clipboard or the user declined paste permissions. This app will hit smler endpoint and based on ip smler will return the user who might have clicked the link
This ensures the most reliable tracking method is chosen automatically.
// App.js
import React, { useEffect } from 'react';
import Clipboard from '@react-native-clipboard/clipboard';
import AsyncStorage from '@react-native-async-storage/async-storage';
export default function App() {
useEffect(() => {
trackOpen();
}, []);
return (
// Your app components
);
}
// Make request to /track/open endpoint
async function trackOpen() {
try {
// Check if this is first launch
const hasLaunched = await AsyncStorage.getItem('app_first_launch');
if (hasLaunched !== null) {
return;
}
await AsyncStorage.setItem('app_first_launch', 'false');
// Check clipboard for deep link
const clipboard = await Clipboard.getString();
let requestBody;
let myDomain = 'go.smler.in'
if (clipboard && clipboard.includes('acme.link')) {
// Clipboard-based tracking
requestBody = { deepLink: clipboard };
// parse the url and get dltHeader and shortCode. dltHeader is optional
} else {
// IP-based tracking fallback
// Will be added soon
}
const response = await fetch(`https://smler.in/api/v1/short?shortCode=${requestBody.shortCode}&domain=${myDomain}&dltHeader=${requestBody.dltHeader}`);
if (response.ok) {
const data = await response.json();
const destinationURL = data.link?.url;
if (destinationURL) {
// Navigate to the destination URL
console.log('Navigating to:', destinationURL);
}
}
} catch (error) {
console.error('Error tracking open:', error);
}
}Handle the navigation
Once you have the deep link URL from your trackOpen function, you can route the user to the appropriate screen.
// App.js with React Navigation
import React, { useEffect, useRef } from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
const Stack = createNativeStackNavigator();
export default function App() {
const navigationRef = useRef();
useEffect(() => {
const handleDeepLink = async () => {
const destinationURL = await trackOpen();
if (destinationURL && navigationRef.current) {
// Parse URL and navigate
const url = new URL(destinationURL);
const path = url.pathname;
if (path.includes("/product/")) {
const productId = path.split("/product/")[1];
navigationRef.current.navigate("Product", { productId });
} else {
navigationRef.current.navigate("Home");
}
}
};
handleDeepLink();
}, []);
return (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Product" component={ProductScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}Important notes
IP-based Tracking Limitations: IP-based tracking relies on the device’s network IP, which can be less reliable in environments like corporate networks, VPNs, or shared Wi-Fi.
Network Operations: Ensure that all API calls are made in background threads to avoid blocking the main thread and ensure smooth app performance.
Error Handling: Implement proper error handling for network requests, clipboard access to ensure the app behaves gracefully under failures.
Testing: Test thoroughly using TestFlight or an internal distribution build to confirm the flow works for both clipboard-based and IP-based tracking scenarios.
App Store Guidelines: Ensure your app complies with Apple's App Store guidelines regarding clipboard access and user privacy.