You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -149,3 +150,5 @@ API layout → OptionsCrawler.crawl() → LayoutProcessor.process()
149
150
- Options that exist in JS types may not be implemented on both platforms — check the presenter
150
151
-`passProps` are stored in JS `Store`, not sent to native (cleared before bridge crossing)
151
152
- The `lib/` folder is generated — never edit it, edit `src/` instead
153
+
- Deep links are processed only after the first `setRoot()` resolves; pre-bridge URLs on iOS are queued natively in `RNNAppDelegate` and flushed on `RCTContentDidAppearNotification` (bridgeless mode — `RCTJavaScriptDidLoadNotification` does NOT fire)
154
+
-`ModalLayoutBuilder` strips React-reserved keys (`ref`, `key`) from URL query params before they reach `passProps`, to avoid React 19 ref-validation crashes
URL-driven navigation is implemented entirely in the JS layer (`src/linking/`) and feeds into the standard command pipeline. Configure with `Navigation.setLinking({ prefixes, config: { screens } })`; matched URLs are presented as modals by default (preserving the user's current navigation state). Customize via `getModal`/`onLink` hooks; gate processing on auth via `isReady`/`setLinkingReady`. Native plumbing — `application:openURL:`, `application:continueUserActivity:`, cold-start URL queueing — lives in `RNNAppDelegate` (iOS) and `NavigationActivity.onNewIntent` (Android), so subclassing the base classes is sufficient. See [Deep Linking docs](https://wix.github.io/react-native-navigation/docs/deep-linking).
78
82
79
83
### Options System
80
84
Styling and behavior is controlled via a hierarchical options object:
Copy file name to clipboardExpand all lines: ios/ARCHITECTURE.md
+5Lines changed: 5 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,6 +28,11 @@ Base class that user's AppDelegate must extend. Handles React Native and navigat
28
28
- Creates `RCTRootViewFactory` and `ReactHost`
29
29
- Calls `[ReactNativeNavigation bootstrapWithHost:]` to initialize navigation
30
30
- Handles RN version differences (0.77, 0.78, 0.79+) via compile-time macros
31
+
- **Deep linking plumbing**:
32
+
- Implements `application:openURL:options:` and `application:continueUserActivity:restorationHandler:`; both call `-dispatchDeepLinkURL:`.
33
+
- `-dispatchDeepLinkURL:` posts `RCTOpenURLNotification` directly if the React runtime is ready, otherwise enqueues the URL.
34
+
- Observes `RCTContentDidAppearNotification` (the Fabric/bridgeless signal) to flush the queue. This solves the cold-start race where URLs (push notifications, OS link launches) arrive before `RCTLinkingManager` is listening.
35
+
- Subclasses can call `-dispatchDeepLinkURL:` manually from notification delegates or any other URL source; the queueing behavior is reused automatically.
Implements URL-to-screen routing on top of the standard command pipeline. The whole feature is JS-only; it consumes URLs from RN's `Linking` module and dispatches `showModal` (or a user-supplied command) when a match is found.
214
+
215
+
**Public surface** (proxied through `NavigationDelegate`):
216
+
-`Navigation.setLinking(config)` — configure prefixes, screen map, and customization hooks
-`Navigation.setLinkingReady(ready)` — user-controlled gate for deferred deep links
219
+
220
+
**Internal modules** (`src/linking/`):
221
+
222
+
| File | Purpose |
223
+
|------|---------|
224
+
|`LinkingHandler.ts`| Orchestrator. Subscribes to `Linking.url`/`getInitialURL`, drives the queue, dispatches matches. Instantiated by `NavigationRoot`. |
|`RouteMatcher.ts`| Compiles `ScreensConfig` into a `RouteNode` tree; matches paths to screen chains with parameter extraction. |
227
+
|`DeferredLinkQueue.ts`| FIFO queue. Holds URLs until both gates open (root-ready + user-ready). |
228
+
|`ModalLayoutBuilder.ts`| Default presentation: wraps the matched chain in a `stack`, merges params into `passProps`, filters React-reserved keys (`ref`, `key`). |
229
+
|`types.ts`| Public types: `LinkingConfig`, `RouteMatch`, `ScreensConfig`, etc. (re-exported via `src/index.ts`). |
230
+
231
+
**Readiness gates** — a URL is dispatched only when both are true:
232
+
1.**Root-ready** (automatic): the first `Navigation.setRoot()` has resolved. `NavigationRoot.setRoot` calls `linkingHandler.setRootReady()` after the promise resolves.
233
+
2.**User-ready** (optional): `config.isReady()` returns `true`, or `setLinkingReady(true)` was called.
234
+
235
+
**Resolution priority** when a match is found:
236
+
1.`config.onLink(match)` — full escape hatch, RNN does nothing else
The query/path keys `ref` and `key` are **not** forwarded as `passProps`. React reserves these names (`ref` is consumed by React itself; passing a string `ref` to a component crashes under React 19's stricter validation). RNN silently drops them and logs a dev-mode warning. Rename any conflicting URL parameters before they reach your links:
By default, every matched link becomes a modal containing a stack with the matched chain pushed in order. If you need to customize either the layout or the navigation command itself, the config exposes two hooks.
@@ -240,11 +249,13 @@ async function onLoginSuccess() {
240
249
Some links don't arrive via the system URL handler — push-notification payloads, branch.io tokens, App Clips. Feed them into the same pipeline with `Navigation.handleDeepLink(url)`:
This runs the URL through parse → match → present exactly like a system-delivered link, including readiness gates.
247
256
257
+
If you'd rather hand the URL off natively (e.g. from a `UNUserNotificationCenterDelegate` you already maintain), call `[self dispatchDeepLinkURL:url]` inside your `RNNAppDelegate` subclass — same behavior, with cold-start queueing built in. See [Native setup → iOS → Notification taps](#ios) for an example.
258
+
248
259
## Complete example
249
260
250
261
```js
@@ -295,11 +306,11 @@ With this configuration:
295
306
296
307
## Native setup
297
308
298
-
The framework consumes URLs from React Native's `Linking` API. You still configure the URL schemes and universal links at the platform level.
309
+
The framework consumes URLs from React Native's `Linking` API. You still configure the URL schemes and universal links at the platform level — but, unlike most React Native apps, you don't need to hand-write `application:openURL:` or `application:continueUserActivity:` glue. As long as your `AppDelegate` subclasses `RNNAppDelegate`, custom-scheme openings and universal links are forwarded to JS automatically, including cold-start URLs that arrive before the React runtime is ready.
299
310
300
311
### iOS
301
312
302
-
Add your URL scheme to`Info.plist`:
313
+
**1. Register your URL scheme in`Info.plist`:**
303
314
304
315
```xml
305
316
<key>CFBundleURLTypes</key>
@@ -313,7 +324,50 @@ Add your URL scheme to `Info.plist`:
313
324
</array>
314
325
```
315
326
316
-
For universal links, add the Associated Domains entitlement and host an `apple-app-site-association` file. See [Apple's documentation](https://developer.apple.com/documentation/xcode/supporting-associated-domains).
327
+
**2. (Optional) Universal links.** Add the Associated Domains entitlement and host an `apple-app-site-association` file — see [Apple's docs](https://developer.apple.com/documentation/xcode/supporting-associated-domains). `RNNAppDelegate` already implements `application:continueUserActivity:restorationHandler:` and forwards browser-activity URLs through the same pipeline.
328
+
329
+
**3. (Optional) Notification taps.** Notifications aren't routed automatically because most apps already own `UNUserNotificationCenter.current.delegate` via Firebase / OneSignal / etc. From your existing notification handler, hand the URL off to RNN:
330
+
331
+
```objc
332
+
#import"AppDelegate.h"// your subclass of RNNAppDelegate
`dispatchDeepLinkURL:` is inherited from `RNNAppDelegate`. It safely handles cold-start (URLs arriving before the React runtime is ready are queued and flushed automatically once content first renders) so you can call it from anywhere — push handlers, deferred-deep-link SDKs, branch.io callbacks, etc.
369
+
370
+
The JS equivalent is `Navigation.handleDeepLink(url)`; use whichever fits the layer you're working at.
317
371
318
372
### Android
319
373
@@ -334,6 +388,8 @@ Add an intent filter to your `MainActivity` in `AndroidManifest.xml`:
334
388
335
389
For App Links, add `android:autoVerify="true"` and host an `assetlinks.json`. See [Android's documentation](https://developer.android.com/training/app-links).
336
390
391
+
`NavigationActivity` already forwards both cold-start (`getIntent()`) and warm (`onNewIntent`) URLs to React Native's `Linking` module — no extra Java/Kotlin needed. Notification taps that carry a deep-link URI (via `PendingIntent` with `ACTION_VIEW`) flow through the same `onNewIntent` path automatically.
0 commit comments