React Native SDK 1.0.1
@limelink/react-native@1.0.1is publicly available on npm.
The wrapper pins Android and iOS native SDK 1.0.1 and exposes one cross-platform incoming-link/result contract, including successful unresolved Universal/App Link delivery.
Requirements
| Requirement | Supported |
|---|---|
| React | 19.1.0 or later |
| React Native | 0.81.0 or later |
| Runtime | Hermes with New Architecture |
| Host | Community CLI or Expo Development Build |
| Android | API 24 or later |
| iOS | iOS 12 or later |
The package runs through Legacy Native Module interoperability on Hermes with the New Architecture. Legacy Architecture, JSC, Expo Go, and web are unsupported. The recorded RN 0.81.5 packed-package matrix passed Android Debug/Release and iOS clean pod installation plus simulator Debug/Release with Xcode 16.4. RN 0.85 Community, RN 0.86 Expo Development Build, and RN 0.86.2 fixtures provide additional matrix evidence. For wrapper 1.0.1 specifically, the current Community CLI fixture verified Android Debug bridge compilation and an iOS simulator Debug build with native SDK 1.0.1; this does not claim a fresh 1.0.1 run of every matrix configuration.
Install
npm install @limelink/react-native@1.0.1
# or: yarn add @limelink/react-native@1.0.1
# or: pnpm add @limelink/react-native@1.0.1
The package resolves the public org.limelink:limelink-aos-sdk:1.0.1 artifact and LimelinkIOSSDK 1.0.1 CocoaPod.
Community CLI Android consumers add the public Maven repository to android/settings.gradle:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://hellovelop.github.io/limelink-aos-sdk-binary/repository")
content { includeGroup("org.limelink") }
}
}
}
Merge this into the existing repository configuration. Templates that prefer project repositories need the same Maven block under allprojects.repositories in android/build.gradle. No GitHub credentials are required.
Expo Development Builds add "plugins": ["@limelink/react-native"]; the plugin configures the repository and warm-link Intent update. Configure app-owned link domains separately and rebuild the native app.
Initialize and listen
import { useEffect } from 'react';
import LimeLink from '@limelink/react-native';
export function LimeLinkSetup() {
useEffect(() => {
const subscription = LimeLink.addLinkListener({
onDeeplinkReceived(result) {
const destination = result.deeplinkUrl ?? result.originalUrl;
if (destination && isAllowedDestination(destination)) {
navigateTo(destination);
}
},
onDeferredDeepLinkNotFound() {},
onDeeplinkError(error) {
showSafeFallback(error.code, error.message, error.platform);
},
});
LimeLink.initialize({
projectId: '550e8400-e29b-41d4-a716-446655440000',
loggingEnabled: false,
});
return () => subscription.remove();
}, []);
return null;
}
Register the listener before initialization so native success replay reaches JavaScript when ready. The public Project UUID belongs in app configuration; Organization API credentials remain server-side credentials.
Platform lifecycle ownership
Android
The native SDK owns cold and warm App Links. Do not read Linking.getInitialURL() and recreate it as an Intent. Keep the current Activity Intent updated:
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
}
iOS
Forward cold and warm URLs directly from AppDelegate/SceneDelegate to the native LimeLinkReactNative helper. Do not forward React Native Linking.getInitialURL() or JavaScript Linking events back into LimeLink.
import LimeLinkReactNative
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else { return false }
return LimeLink.handleIncomingLink(url)
}
For an AppDelegate-owned custom scheme, add:
func application(
_ app: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
return LimeLink.handleIncomingLink(url)
}
Scene-based apps instead forward their scene's cold/warm URLs in SceneDelegate (with import LimeLinkReactNative):
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
// Keep the host's existing window/React Native setup here.
for context in connectionOptions.urlContexts {
_ = LimeLink.handleIncomingLink(context.url)
}
for activity in connectionOptions.userActivities {
if activity.activityType == NSUserActivityTypeBrowsingWeb,
let url = activity.webpageURL {
_ = LimeLink.handleIncomingLink(url)
}
}
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL {
_ = LimeLink.handleIncomingLink(url)
}
}
func scene(_ scene: UIScene, openURLContexts contexts: Set<UIOpenURLContext>) {
for context in contexts { _ = LimeLink.handleIncomingLink(context.url) }
}
Merge callbacks into the lifecycle owner used by your host; do not forward the same event from both delegates or replace existing React Native startup code. Configure Associated Domains and URL schemes as described in the iOS guide.
LimeLink.handleIncomingLink(url) remains available in JavaScript only for a legitimate explicit app-owned handoff; it is not the normal iOS lifecycle integration mechanism.
Result contract
type LimeLinkResult = {
deeplinkUrl: string | null;
source: 'directDeepLink' | 'universalLink' | 'deferredDeepLink';
originalUrl: string | null;
isDeferred: boolean;
};
For resolved results, route deeplinkUrl. An unresolved Universal/App Link is a successful callback with deeplinkUrl: null, the exact inbound HTTPS URL in originalUrl, source: 'universalLink', and isDeferred: false; it does not emit a separate error callback. If the app chooses browser fallback, use deeplinkUrl ?? originalUrl, then parse and exactly compare the selected URL's scheme and host to an app-owned allowlist.
Native SDKs own pre-initialization buffering, exact-URL five-second deduplication, and capacity-10 successful-result replay. The wrapper signals readiness when the first JS listener is present. Errors and deferred not-found outcomes are live-only.
Deferred deep links
const outcome = await LimeLink.handleDeferredDeepLink();
if (outcome.status === 'matched') {
if (isAllowedDestination(outcome.result.deeplinkUrl)) {
navigateTo(outcome.result.deeplinkUrl);
}
} else if (outcome.status === 'failed') {
showSafeFallback(outcome.error);
}
The status is exactly matched, notFound, or failed. Native listeners also receive manual outcomes, so choose either the Promise or listener as navigation owner. Malformed native outcomes and bridge failures can reject the Promise.
Public API
initialize(config: LimeLinkConfig): void
isInitialized(): boolean
addLinkListener(listener: LimeLinkListener): LimeLinkSubscription
handleIncomingLink(url: string): void
handleDeferredDeepLink(): Promise<DeferredDeepLinkOutcome>
Verify and Troubleshoot
- Community CLI: verify native Android/iOS dependencies resolve, register the JavaScript listener before initialization, and test each platform's native lifecycle path.
- Expo: use an Expo Development Build with the package plugin; Expo Go is unsupported. Rebuild the native app after config or native dependency changes.
- iOS: reinstall pods when necessary and forward AppDelegate/SceneDelegate URLs to the native helper once.
- Android: keep
setIntent(intent)inonNewIntentand do not recreateLinking.getInitialURL()as a new Intent.
Test direct, resolved web, unresolved web, deferred matched, notFound, and failed outcomes. If navigation happens twice, confirm only one native lifecycle path owns ingress and only the Promise or listener owns a manual deferred result.
Migrating from 1.0.0
- Treat
deeplinkUrlas nullable. - Handle unresolved Universal/App Links as successful results and use the exact
originalUrlonly for an allowlisted browser fallback.
Migrating from 0.1.1
- Replace removed
handleUniversalLink(url)withhandleIncomingLink(url)for explicit handoffs. - Replace
resolvedUriwith nullabledeeplinkUrl. - Remove
queryParamsandpathParams; parsedeeplinkUrlas needed. - Handle required
sourceand nullableoriginalUrl. - Replace iOS React Native
Linkingforwarding with direct native lifecycle forwarding. - Remove JavaScript replay assumptions; native SDKs own success buffering and deduplication.
- Refresh the package and pinned native dependencies to
1.0.1, reinstall pods, and rebuild.