In a bare React Native app, what native wiring lets incoming links reach the Linking module on iOS and on Android?
answer
- AppDelegate forwards two callbacks
- RCTLinkingManager open url
- continue userActivity for https links
- launchOptions into startReactNative
- MainActivity launchMode singleTask
basics
~10 sOn iOS the AppDelegate forwards the open-URL and continue-user-activity callbacks to RCTLinkingManager and passes launchOptions to startReactNative; on Android, MainActivity uses launchMode singleTask so running-app links arrive through onNewIntent.
solid answer
~40 sOn **iOS**, the app delegate must hand links to `RCTLinkingManager`: `application(_:open:options:)` for custom schemes and `application(_:continue:restorationHandler:)` for Universal Links, each calling the matching `RCTLinkingManager.application(...)` class method, which posts the URL as the `'url'` event. For cold starts, the app must pass its `launchOptions` into `startReactNative(withModuleName:in:launchOptions:)`, because `getInitialURL()` reads the launch URL from them. On **Android**, the manifest's `MainActivity` uses `android:launchMode="singleTask"`, so a link to a running app is delivered to the existing activity's `onNewIntent`, which React Native turns into the `'url'` event; without it Android can start a second activity instance. Cold starts need nothing extra: `getInitialURL()` reads the launching `VIEW` intent. Expo's prebuild templates already contain all of this.
code
swift · 19 linesfunc application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
return RCTLinkingManager.application(app, open: url, options: options)
}
func application(
_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
return RCTLinkingManager.application(
application,
continue: userActivity,
restorationHandler: restorationHandler
)
}go deeper
Recall that iOS needs the AppDelegate to forward links to RCTLinkingManager and that Android's MainActivity uses launchMode singleTask.
Explain which delegate callback carries schemes versus Universal Links, why launchOptions feed getInitialURL, and how singleTask routes a warm link through onNewIntent.
Diagnose missing links by splitting cold and warm paths per platform, and catch regressions from rewritten AppDelegates, brownfield hosts and splash activities.
In brownfield or multi-entry apps, decide which native component owns link intake so JavaScript receives every link exactly once.
## Why native wiring is needed at all `Linking` is a JavaScript facade over two native modules: `RCTLinkingManager` on iOS and the intent module on Android. The operating system delivers URLs to **native** entry points, the app delegate on iOS and the activity on Android. If those entry points do not pass the URL along, JavaScript never hears about it. A newly generated React Native or Expo project has the wiring already; it goes missing when an `AppDelegate` is rewritten, a native app is brownfielded, or a splash activity is added. ## iOS: the app delegate | OS entry point | When it fires | Forward to | |---|---|---| | `application(_:open:options:)` | a custom-scheme URL opens a running app | `RCTLinkingManager.application(app, open: url, options: options)` | | `application(_:continue:restorationHandler:)` | a Universal Link opens a running app | `RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)` | | `launchOptions` in `didFinishLaunchingWithOptions` | the link launches a killed app | pass into `startReactNative(withModuleName:in:launchOptions:)` | Details worth knowing: - The two class methods post a notification that the `RCTLinkingManager` module turns into the `'url'` event. For a user activity it only forwards web-browsing activities that carry a `webpageURL`. - `getInitialURL()` reads the launch options: the URL key for a custom scheme, or the user-activity dictionary for a Universal Link. An app delegate that passes `nil` launch options makes every cold-start link resolve to `null`. - In Expo SDK 57 the generated `AppDelegate` subclasses `ExpoAppDelegate`, passes `launchOptions`, and forwards both callbacks to `RCTLinkingManager` alongside `super`. ## Android: launchMode singleTask On Android the link arrives in an `Intent` with action `VIEW`: 1. **Cold start.** The intent launches `MainActivity`. `getInitialURL()` reads the current activity's intent data when the action is `VIEW` and resolves it. No extra code is needed. 2. **Warm start with `singleTask`.** Android reuses the existing `MainActivity` and calls its `onNewIntent`. `ReactActivity` passes that to the React host, which emits the intent's data as the `'url'` event to the running JavaScript. 3. **Warm start without it.** With the default `standard` launch mode, Android can create a **second** `MainActivity` instance on top of the first. The user sees the app seem to restart, the existing screen's `'url'` listener never fires, and pressing back may reveal the old copy. That is why the React Native docs and Expo's generated manifest set `android:launchMode="singleTask"` on `MainActivity`, and why Expo's scheme config plugin refuses to add schemes unless a `singleTask` activity exists. Two Android traps: - `onNewIntent` does not replace the activity's stored intent in React Native's delegate, so `getInitialURL()` keeps returning the original launch URL; the new link exists only as the event. - A separate splash activity declared as the link target must forward the intent's data to `MainActivity`, or the cold-start link is lost before React Native starts. ## How to check the wiring quickly - Kill the app, tap a link, and log `await Linking.getInitialURL()`. A `null` on iOS points at launch options; on Android, at which activity received the intent. - With the app open, tap a link and log inside the `'url'` handler. Silence on iOS points at the app delegate callbacks; a restart-like flash on Android points at the launch mode. - Test both a custom scheme and an `https` link on iOS, because they arrive through different delegate methods. ## Expo projects In an Expo project with Continuous Native Generation, prebuild writes this wiring for you. The generated `AndroidManifest.xml` gives `MainActivity` `android:launchMode="singleTask"`, and the SDK 57 `AppDelegate` passes `launchOptions` to the factory and forwards both link callbacks. The risk returns when a team edits the native folders by hand, or when a config plugin rewrites the app delegate or manifest: a plugin that replaces `MainActivity`'s attributes or the delegate's link methods can silently remove the forwarding. After adding such a plugin, rerun the checks above on a fresh prebuild. ## What is not wiring Registering schemes (`CFBundleURLTypes`, intent filters) and verifying domains decide whether the OS sends the link to your app at all. The wiring here decides whether a link the OS already delivered reaches JavaScript.
- Links work when the React Native iOS app is killed but not when it is already open; where do you look?The cold path reads launch options, so it works even without delegate forwarding. The warm path needs the app delegate to call `RCTLinkingManager` from `application(_:open:options:)` for schemes and from `application(_:continue:restorationHandler:)` for Universal Links. Check which kind of link fails: a missing continue-user-activity override breaks only `https` links, while a missing open-URL override breaks only custom schemes.
- Why can a splash activity break cold-start deep links on Android?`getInitialURL()` reads the intent of the activity React Native is attached to. If the link's intent filter sits on a splash activity that then starts `MainActivity` with a fresh intent, the data is dropped and `getInitialURL()` resolves to `null`. Either put the filters on `MainActivity` or copy the intent's data and action into the intent that starts it.
saying these in an interview costs you the question
- Linking works on iOS without any AppDelegate code
- The continue-userActivity callback is only needed for custom schemes
- singleTask is needed for cold starts to deliver getInitialURL
- The default standard launchMode delivers links to the running activity
- Passing nil launchOptions only affects push notifications