Skip to main content
Documentation
SDKsReact Native

React Native SDK

Integrate the WarpLink React Native SDK for Universal Links on iOS, App Links on Android, deferred deep links, and install attribution.

Requirements: React Native 0.75+, React 18+, iOS 15+ and/or Android API 26+. This SDK requires native modules and does not work with Expo Go. Use a development build.

Source: github.com/WarpLinkApp/warplink-react-native-sdk

Installation

The iOS SDK is distributed through Swift Package Manager and needs dynamic linkage, so add this to ios/Podfile inside your app target before installing pods:

Platform Setup

The React Native SDK bridges to native iOS and Android SDKs. Both platforms need configuration.

iOS

  1. Open Xcode, select your app target > Signing & Capabilities
  2. Add Associated Domains > applinks:yourapp.aplnk.to
  3. Enable Associated Domains in your App ID at developer.apple.com

Find your app's subdomain in the dashboard: open the app, then Settings. Or see Your app's own link address.

For Expo, add to app.json:

Android

Add the intent filter to your main Activity in android/app/src/main/AndroidManifest.xml:

For Expo, add to app.json:

Serving links from a custom domain? Add it to the associated domains and the intent filter alongside yourapp.aplnk.to, then declare it in configure().

Every app has its own subdomain, yourapp.aplnk.to. Add that exact host, never a wildcard such as applinks:*.aplnk.to on iOS. If your app existed before subdomains, keep aplnk.to listed next to the new address: links you already have stay on aplnk.to and keep opening your app. See Your app's own link address.

iOS AppDelegate Hook

iOS does not hand Universal Links to native modules on its own. Add the WarpLink call to the delegate methods you already have, and leave their return values to your existing linking code.

handleIncomingURL returns nothing and does no domain filtering, so never let it decide your delegate's return value. Keep returning whatever your existing forwarding returns, or the deep links your app already handles stop arriving. Forwarding every URL is safe: the SDK tries to resolve each one and silently drops anything that is not a WarpLink link, so custom schemes and OAuth callbacks never reach the onLink you passed to configure(). An explicit onDeepLink() subscriber can still receive them, as an { error } event with code E_INVALID_URL.

The Swift module name is the pod name with hyphens replaced by underscores (warplink_react_native). If your delegate forwards to super as well as a linking library, keep every call and store each result in a local before you combine them. A || chain short-circuits, so a handler placed after one that returns true never runs.

Expo

A plain npx expo prebuild layers its changes onto the native files already there, so a hand-edited AppDelegate usually survives it, but npx expo prebuild --clean deletes the native directories first and any build machine that starts without ios/ regenerates the file from the template. In both of those cases the call you added by hand is gone. Pick one:

  • Commit the generated ios/ directory and add the calls to the AppDelegate there.
  • Add the calls from an Expo config plugin with withAppDelegate, so they survive every prebuild.

Expo Go cannot load native modules, so a development build is required either way.

Android needs no host code: configure() handles cold start automatically, and warm start works once your launch Activity uses android:launchMode="singleTask".

iOS SceneDelegate Hook

Skip this section unless your app declares a UIApplicationSceneManifest in Info.plist and has a SceneDelegate that owns the window. That is the default shape of a plain Xcode single-view app template. Apple requires the scene life cycle for apps built with its newer SDKs: an app built without one does not launch at all. Once a scene manifest exists, iOS routes Universal Link and custom-scheme delivery to the scene, and stops calling the AppDelegate hook above entirely. The AppDelegate methods stay in the app, but never run again. Nothing errors, nothing crashes, and configure() still reports the SDK as healthy. The app only stops resolving WarpLink links, silently.

Add the same call to the SceneDelegate methods that receive delivery once a scene owns the window: scene(_:willConnectTo:options:) for cold start, scene(_:continue:) for a Universal Link on a warm start, and scene(_:openURLContexts:) for a custom scheme on a warm start. Keep the AppDelegate hook too: it is harmless once a scene exists, and an app that later turns scenes off still needs it.

Register the delegate the way any React Native scene-based app does: name this class in Info.plist's UIApplicationSceneManifest ($(PRODUCT_MODULE_NAME).SceneDelegate in Swift, the plain class name in Objective-C), or return it from application(_:configurationForConnecting:options:) if you configure scenes in code. Neither step is specific to WarpLink.

handleIncomingURL still returns nothing and does no domain filtering here either, so forwarding every url from these methods is exactly as safe as forwarding every url from the AppDelegate hook, for the same reason: the SDK resolves what it recognizes and silently drops the rest.

React Native's own Linking API. If you also forward to RCTLinkingManager from these scene methods (RCTLinkingManager.application(_:continue:restorationHandler:), RCTLinkingManager.application(_:open:options:)), a warm start keeps Linking.addEventListener('url', ...) working: neither call depends on which delegate makes it. Cold-start links reach your app through the SceneDelegate hook above.

Configure the SDK

Call configure() once at app startup, outside of any React component. Pass an onLink callback and the SDK wires cold start, warm start, and the deferred deep link check into that single sink.

The credential below is an SDK key, created in the dashboard under Keys > SDK keys. Configure the SDK with an SDK key, since only an SDK key records installs. If your organization has several apps, create the key for this app: a key tied to one app is refused for any other app with KEY_APP_MISMATCH.

configure() returns a Promise<void>. A configuration failure is delivered to onLink as { error } when you pass onLink, as in the example above. Only when you omit onLink does the returned promise reject with the error instead, so await it in that case:

The SDK always recognizes aplnk.to. List your app's subdomain, yourapp.aplnk.to, and any custom domain you serve links from in linkDomains. configure() passes the list straight to the native iOS and Android SDKs, so a link on those hosts resolves on the very first launch, with no network round trip.

With linkDomains set, your own links resolve inside the SDK on the very first launch.

The native declaration works too, and is read before any JavaScript runs: a WarpLinkDomains array in Info.plist on iOS, an app.warplink.DOMAINS meta-data entry in AndroidManifest.xml on Android. See the iOS and Android guides. Expo apps can set the iOS key through expo.ios.infoPlist in app.json, and the Android entry from a config plugin.

Every form is optional and additive. Domains are normalized natively (trimmed, lowercased, a full URL reduced to its host), so paste whichever form you have.

Opt Out of Automatic Handling

Both pieces are on by default. Disable either to drive it yourself with the manual methods:

Testing

iOS: Physical Device Required

  1. Build and run on a physical device
  2. Paste your test link (e.g., https://yourapp.aplnk.to/abc123) into Notes or Messages and tap it. Typing it into the Safari address bar never opens the app.
  3. The app should open and trigger the deep link callback
  4. Check Xcode console for [WarpLink] log messages

On the iOS Simulator, xcrun simctl openurl booted https://yourapp.aplnk.to/abc123 opens the app only when its Associated Domains entitlement lists the host and the domain serves a valid apple-app-site-association file that matches the path. Confirm the iOS path on a physical device before you ship.

Android: Physical Device or Emulator

Android App Links work on both a physical device and the emulator, unlike iOS Universal Links.

Testing a deferred deep link, meaning a click that happens before the app is installed, needs a delete/uninstall-then-reinstall cycle so the SDK treats it as a fresh install. See Testing in the Deferred Deep Links guide.

Next Steps