@appfastfly/react-native
v1.0.6
Published
Appfastfly React Native SDK - Mobile Deep Linking & Attribution
Maintainers
Readme
@appfastfly/react-native
React Native SDK for Appfastfly — mobile deep linking & attribution.
Features
- Deep Linking — Universal Links (iOS) + App Links (Android) + URI schemes
- Deferred Deep Links — attribute installs to the original link via device fingerprint, clipboard token, and install referrer
- Link Creation — generate trackable short links with custom payloads and Open Graph metadata
- User Identity — associate devices with user IDs for cross-device attribution
- Event-driven — subscribe to deep link events with automatic caching
Requirements
| Platform | Minimum version | | ------------ | ---------------------------------------- | | iOS | 15.0+ | | Android | API 24+ (7.0) | | React Native | 0.76+ (New Architecture / TurboModules) |
Note: This SDK requires the New Architecture (TurboModules) which is enabled by default starting from React Native 0.76. It does not support the old architecture (Bridge).
Installation
yarn add @appfastfly/react-native
# or
npm install @appfastfly/react-nativeiOS — install CocoaPods:
cd ios && bundle exec pod installAndroid — no additional steps, Gradle auto-links the module.
1. Native Configuration
iOS — Info.plist
<key>AppfastflyApiKey</key>
<string>YOUR_API_KEY</string>Optionally override the service URL (defaults to https://api.appfastfly.io.vn):
<key>AppfastflyServiceUrl</key>
<string>https://your-custom-api.com</string>Android — AndroidManifest.xml
Add inside the <application> tag:
<meta-data
android:name="com.appfastfly.API_KEY"
android:value="YOUR_API_KEY" />Optionally override the service URL:
<meta-data
android:name="com.appfastfly.SERVICE_URL"
android:value="https://your-custom-api.com" />Get your API key from the Appfastfly Dashboard.
2. Deep Links Setup
iOS
Associated Domains
In Xcode > target > Signing & Capabilities > Associated Domains, add:
applinks:link.yourdomain.comAppDelegate (Objective-C)
// AppDelegate.mm
#import <AppfastflyDeepLinkModule.h>
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *))handler
{
[AppfastflyDeepLinkModule continueUserActivity:userActivity];
return YES;
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options
{
[AppfastflyDeepLinkModule openURL:url];
return YES;
}AppDelegate (Swift)
// AppDelegate.swift
import AppfastflyDeepLinkModule
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
AppfastflyDeepLinkModule.continue(userActivity)
return true
}
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
AppfastflyDeepLinkModule.open(url)
return true
}URL Scheme (recommended)
In Xcode > target > Info > URL Types, add your custom scheme (e.g. myapp).
Android
AndroidManifest.xml — Intent Filters
Add inside your MainActivity tag:
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:exported="true">
<!-- App Links -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https"
android:host="link.yourdomain.com" />
</intent-filter>
<!-- URI Scheme -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
</activity>MainActivity (Kotlin)
import android.content.Intent
import android.os.Bundle
import com.appfastfly.deeplink.AppfastflyDeepLinkModule
class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AppfastflyDeepLinkModule.handleIntent(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
AppfastflyDeepLinkModule.handleIntent(intent)
}
}3. Usage
Initialize
Call once at app startup, before any navigation:
import { Appfastfly } from '@appfastfly/react-native';
await Appfastfly.init();Subscribe to Deep Links
const unsubscribe = Appfastfly.subscribe((event) => {
console.log('Payload:', event.payload);
console.log('First session?', event.isFirstSession);
if (event.payload.screen) {
navigation.navigate(event.payload.screen, event.payload);
}
});
// Clean up when done
unsubscribe();Full Example
import React, { useEffect } from 'react';
import { Appfastfly } from '@appfastfly/react-native';
function App() {
useEffect(() => {
Appfastfly.init();
const unsubscribe = Appfastfly.subscribe((event) => {
if (event.payload.productId) {
navigation.navigate('Product', { id: event.payload.productId });
}
});
return unsubscribe;
}, []);
return <MainNavigator />;
}Read Cached Params
const latest = Appfastfly.getLatestParams();
const first = Appfastfly.getFirstParams();Create Links
const link = await Appfastfly.createLink({
payload: { screen: 'Product', productId: '123' },
channel: 'social',
campaign: 'summer-sale',
ogTitle: 'Check out this product!',
ogDescription: 'Get 20% off with this link',
ogImageUrl: 'https://example.com/product.jpg',
});
console.log(link.url);
// → https://link.myapp.com/l/abc123User Identity
// Associate device with a user
await Appfastfly.setIdentity('user-456');
// Remove association
await Appfastfly.logout();4. API Reference
Appfastfly.init()
Initialize the SDK. Must be called once before any other method. Reads configuration from native (Info.plist / AndroidManifest).
Returns: Promise<void>
Appfastfly.subscribe(listener)
Listen for deep link events. If params are already available, fires the callback immediately.
| Parameter | Type | Description |
| ---------- | ------------------------------- | ------------------- |
| listener | (event: DeepLinkEvent) => void | Callback on event |
Returns: () => void — unsubscribe function
interface DeepLinkEvent {
url?: string;
payload: Record<string, any>;
matchMethod?: string;
matchConfidence?: number;
isFirstSession: boolean;
}Appfastfly.getLatestParams()
Returns the most recent deep link payload, or null.
Appfastfly.getFirstParams()
Returns the payload from the install-attributed deep link (first session only), or null.
Appfastfly.createLink(params)
Create a trackable short link.
interface CreateLinkParams {
payload: Record<string, any>;
ogTitle?: string;
ogDescription?: string;
ogImageUrl?: string;
channel?: string;
campaign?: string;
tags?: string[];
iosRedirectUrl?: string;
androidRedirectUrl?: string;
webRedirectUrl?: string;
expiresAt?: string;
}Returns: Promise<{ shortCode: string; url: string }>
Appfastfly.setIdentity(userId)
Associate the current device with a user ID for cross-device attribution.
Appfastfly.logout()
Remove the current device-user association.
5. Troubleshooting
Universal Links not opening the app (iOS)
- Verify
applinks:in Associated Domains matches your link domain exactly - Ensure the Apple App Site Association (AASA) file is served over HTTPS with a valid certificate
- Long-press a link in Safari to verify "Open in App" appears
App Links not opening the app (Android)
- Verify
assetlinks.jsonis accessible athttps://your-domain/.well-known/assetlinks.json - The SHA-256 fingerprint must match your signing certificate
android:autoVerify="true"must be set on the intent-filter
No events in subscribe()
- Ensure
init()is called beforesubscribe() - Verify
continueUserActivity:(iOS) orhandleIntent()(Android) is wired in your AppDelegate / MainActivity - Check native logs for
[Appfastfly]warnings
Contributing
See the contributing guide to learn how to contribute to the repository and the development workflow.
License
MIT
