React Native SDK 1.0.0

Deployment status: @limelink/react-native@1.0.0 is not yet available on npm; public latest remains 0.1.1. Use this guide to prepare the 1.0.0 migration, and run the install command only after npm publishes 1.0.0.

The wrapper pins Android and iOS native SDK 1.0.0 and exposes one cross-platform incoming-link/result contract.

Requirements

RequirementSupported
React19.1.0 or later
React Native0.81.0 or later
RuntimeHermes with New Architecture
HostCommunity CLI or Expo Development Build
AndroidAPI 24 or later
iOSiOS 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. RN 0.81.5 Android and clean pod installation have passed, while its iOS simulator row remains pending. RN 0.85 Community, RN 0.86 Expo Development Build, and RN 0.86.2 packed-package fixtures provide additional build evidence.

Install after publication

npm install @limelink/react-native@1.0.0
# or: yarn add @limelink/react-native@1.0.0
# or: pnpm add @limelink/react-native@1.0.0

The package resolves org.limelink:limelink-aos-sdk:1.0.0 and LimelinkIOSSDK 1.0.0. The Android Maven artifact is public; the iOS CocoaPod and npm wrapper must both be publicly resolvable before a clean React Native iOS install can succeed.

Android consumers add the LimeLink public Maven repository. Expo Development Builds add "plugins": ["@limelink/react-native"]; the app configures its owned link domains separately.

Initialize and listen

import { useEffect } from 'react';
import LimeLink from '@limelink/react-native';

export function LimeLinkSetup() {
  useEffect(() => {
    const subscription = LimeLink.addLinkListener({
      onDeeplinkReceived(result) {
        if (isAllowedDestination(result.deeplinkUrl)) {
          navigateTo(result.deeplinkUrl);
        }
      },
      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)
}

Call the same native helper from SceneDelegate cold/warm methods and app-owned custom-scheme callbacks.

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;
  source: 'directDeepLink' | 'universalLink' | 'deferredDeepLink';
  originalUrl: string | null;
  isDeferred: boolean;
};

deeplinkUrl is the sole routing authority. Parse it and exactly compare its scheme and host to an app-owned allowlist before navigation.

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.

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>

Migrating from 0.1.1