react-native-navigation
Version:
React Native Navigation - truly native navigation for iOS and Android
417 lines (336 loc) • 16.6 kB
Markdown
# iOS Native Architecture
The iOS implementation provides native navigation using UIKit view controllers, coordinated through a bridge module that receives commands from JavaScript.
## App Integration
### RNNAppDelegate
**Files**: `RNNAppDelegate.h/mm`
Base class that user's AppDelegate must extend. Handles React Native and navigation initialization:
```objc
// AppDelegate.h - User extends RNNAppDelegate
#import "RNNAppDelegate.h"
AppDelegate : RNNAppDelegate
// AppDelegate.m - User implements sourceURLForBridge
AppDelegate
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
}
```
**What RNNAppDelegate does:**
- Sets up React Native feature flags (Fabric, TurboModules, Bridgeless)
- Creates `RCTRootViewFactory` and `ReactHost`
- Calls `[ReactNativeNavigation bootstrapWithHost:]` to initialize navigation
- Handles RN version differences (0.77, 0.78, 0.79+) via compile-time macros
### ReactNativeNavigation Bootstrap
**File**: `ReactNativeNavigation.h/mm`
Public API for native initialization:
```objc
// New architecture (0.77+)
[ReactNativeNavigation bootstrapWithHost:reactHost];
// Legacy bridge
[ReactNativeNavigation bootstrapWithBridge:bridge];
// Register native screens
[ReactNativeNavigation registerExternalComponent:@"NativeScreen"
callback:^(NSDictionary *props, RCTBridge *bridge) {
return [[MyNativeVC alloc] init];
}];
```
### Autolink Script (`npx rnn-link`)
The `autolink/postlink/postLinkIOS.js` script automates setup:
| Linker | What it does |
|--------|--------------|
| `AppDelegateLinker` | Changes AppDelegate to extend `RNNAppDelegate`, imports ReactNativeNavigation, removes RCTRootView setup |
| `PodfileLinker` | Adds required pods configuration |
| `PlistLinker` | Updates Info.plist if needed |
**AppDelegateLinker transformations:**
- Swift: `class AppDelegate: RCTAppDelegate` → `class AppDelegate: RNNAppDelegate`
- Obj-C header: `@interface AppDelegate : RCTAppDelegate` → `@interface AppDelegate : RNNAppDelegate`
- Imports `<ReactNativeNavigation/ReactNativeNavigation.h>`
- Removes manual RCTRootView/window setup (navigation manages the window)
## Directory Structure
```
ios/
├── RNNBridgeModule.h/mm # Bridge entry point (legacy architecture)
├── RNNBridgeManager.h/mm # Bridge initialization
├── RNNCommandsHandler.h/mm # Command dispatcher
├── RNNEventEmitter.h/mm # Event emission to JS
├── RNNLayoutManager.h/mm # View controller tracking
├── RNNLayoutNode.h/mm # Layout tree parsing
├── RNNNavigationOptions.h/mm # Options model
├── RNNComponentViewController.h/mm # React component screen
├── RNNStackController.h/mm # Stack navigation
├── RNNBottomTabsController.h/mm # Tab navigation
├── RNNSideMenuViewController.h/mm # Side menu
├── RNNTopTabsViewController.h/mm # Top tabs
├── RNNSplitViewController.h/mm # Split view (iPad)
├── RNNModalManager.h/mm # Modal presentation
├── RNNOverlayManager.h/mm # Overlay management
├── TurboModules/ # New architecture entry points
│ ├── RNNTurboModule.h/mm # TurboModule spec implementation
│ ├── RNNTurboManager.h/mm # Manager for host, window, external components
│ ├── RNNTurboCommandsHandler.h/mm # TurboModule command routing
│ └── RNNTurboEventEmitter.h/mm # Event emission for new arch
├── Utils/ # Utility classes
├── RNNSideMenu/ # MMDrawerController integration
└── ReactNativeNavigation.xcodeproj/
```
## Bridge Architecture
The library supports both the legacy bridge and new TurboModule architecture:
- **Legacy (Bridge)**: `RNNBridgeModule` receives commands via `RCT_EXPORT_METHOD`
- **New Architecture**: `RNNTurboModule` (in `TurboModules/`) receives commands directly
Both entry points delegate to `RNNCommandsHandler` for command execution.
```
┌────────────────────────────────────────────────────────────┐
│ JavaScript (TurboModule) │
└─────────────────────────┬──────────────────────────────────┘
│
┌─────────────────────────▼──────────────────────────────────┐
│ RNNBridgeModule (bridge) / RNNTurboModule (new arch) │
│ - setRoot, push, pop, showModal, dismissModal, etc. │
└─────────────────────────┬──────────────────────────────────┘
│
┌─────────────────────────▼──────────────────────────────────┐
│ RNNCommandsHandler │
│ - Validates commands, manages layout lifecycle │
│ - Coordinates with managers │
└──────┬──────────────┬──────────────┬──────────────┬────────┘
│ │ │ │
┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐ ┌─────▼──────┐
│RNNLayout │ │RNNModal │ │RNNOverlay │ │RNNViewController│
│Manager │ │Manager │ │Manager │ │Factory │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
```
## View Controller Hierarchy
All navigation view controllers conform to `RNNLayoutProtocol`:
```
UIViewController
├── RNNComponentViewController # React component screen
├── RNNExternalViewController # Native screen wrapper
└── RNNSplashScreenViewController # Launch screen
UINavigationController
└── RNNStackController # Stack navigation
UITabBarController
└── RNNBottomTabsController # Bottom tabs
UIViewController (custom)
├── RNNTopTabsViewController # Horizontal top tabs
└── RNNSideMenuChildViewController # Drawer child
MMDrawerController
└── RNNSideMenuViewController # Side menu/drawer
UISplitViewController
└── RNNSplitViewController # Master-detail (iPad)
```
## Core Classes
### RNNBridgeModule
Entry point for JavaScript commands (legacy architecture). Exports methods via `RCT_EXPORT_METHOD`:
```objc
RCT_EXPORT_METHOD(setRoot:(NSString*)commandId layout:(NSDictionary*)layout
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject);
```
`RNNBridgeModule` returns the main queue as its `methodQueue`; `RNNTurboModule` uses `RCTExecuteOnMainQueue` for each command to ensure UI operations run on the main thread.
### RNNBridgeManager
Initializes bridge infrastructure:
- Creates RNNLayoutManager, RNNModalManager, RNNOverlayManager
- Creates RNNViewControllerFactory, RNNCommandsHandler
- Provides extra modules to React bridge
- Handles JavaScript reload events
### RNNCommandsHandler
Central command dispatcher implementing all navigation operations:
- `setRoot:` - Replace root view controller
- `push:componentId:layout:` - Push to stack
- `pop:componentId:` - Pop from stack
- `showModal:` - Present modal
- `dismissModal:` - Dismiss modal
- `showOverlay:` - Show overlay window
- `dismissOverlay:` - Hide overlay
### RNNLayoutManager
Tracks active view controllers:
- `addPendingViewController:` - Register during creation
- `removePendingViewController:` - Cleanup after presentation
- `findComponentForId:` - Lookup by componentId
### RNNLayoutNode
Parses JSON layout from JavaScript:
- Determines type via predicates: `isComponent`, `isStack`, `isTabs`, etc.
- Holds `type`, `nodeId`, `data` (options), `children`
### RNNViewControllerFactory
Creates view controllers from RNNLayoutNode:
- `createStack:` → RNNStackController
- `createBottomTabs:` → RNNBottomTabsController
- `createComponent:` → RNNComponentViewController
- `createSideMenu:` → RNNSideMenuViewController
## RNNLayoutProtocol
Protocol all navigation controllers implement:
```objc
@protocol RNNLayoutProtocol
- (instancetype)initWithLayoutInfo:(RNNLayoutInfo*)layoutInfo
creator:(id<RNNComponentViewCreator>)creator
options:(RNNNavigationOptions*)options
defaultOptions:(RNNNavigationOptions*)defaultOptions
presenter:(RNNBasePresenter*)presenter
eventEmitter:(RNNEventEmitter*)eventEmitter
childViewControllers:(NSArray*)childViewControllers;
- (void)render;
- (UIViewController*)getCurrentChild;
- (void)mergeOptions:(RNNNavigationOptions*)options;
- (RNNNavigationOptions*)resolveOptions;
- (void)setDefaultOptions:(RNNNavigationOptions*)defaultOptions;
- (void)onChildWillAppear;
- (void)readyForPresentation;
- (CGFloat)getTopBarHeight;
- (CGFloat)getBottomTabsHeight;
@end
```
Additional methods provided via `UIViewController+LayoutProtocol` category:
- `destroy`, `topMostViewController`, `stack`
- `componentWillAppear`, `componentDidAppear`, `componentDidDisappear`
- `screenPopped`, `prepareForTransition`
- `resolveOptionsWithDefault`, `mergeChildOptions:child:`
## Presenter Pattern
Each view controller type has a corresponding presenter that applies options:
| Controller | Presenter |
|------------|-----------|
| RNNComponentViewController | RNNComponentPresenter |
| RNNStackController | RNNStackPresenter + TopBarPresenter |
| RNNBottomTabsController | RNNBottomTabsPresenter + BottomTabPresenter |
| RNNSideMenuViewController | RNNSideMenuPresenter |
| RNNSplitViewController | RNNSplitViewControllerPresenter |
### RNNBasePresenter
Base class with lifecycle methods:
- `applyOptionsOnInit:` - Initial setup
- `applyOptions:` - Apply current options
- `mergeOptions:resolvedOptions:` - Runtime updates
- `componentWillAppear`, `componentDidAppear`, `componentDidDisappear`
## React View Integration
### RNNReactView
Wraps RCTRootView (legacy) or RCTSurfaceHostingView (new architecture):
- Implements `RNNComponentProtocol`
- Manages `componentId`, `componentType`, `moduleName`
- Lifecycle: `componentWillAppear`, `componentDidAppear`, `componentDidDisappear`
### RNNReactRootViewCreator
Creates RNNReactView instances:
- Supports component types: Component, TopBarTitle, TopBarButton, TopBarBackground
- Handles size flexibility for flexible layouts
### RNNReactComponentRegistry
Caches created React component instances:
- `createComponentIfNotExists:parentComponentId:componentType:reactViewReadyBlock:`
- `removeComponent:`, `clearComponentsForParentId:`
## Options System
### RNNNavigationOptions
Master options object containing all configuration:
```objc
@interface RNNNavigationOptions : RNNOptions
@property RNNTopBarOptions* topBar;
@property RNNBottomTabsOptions* bottomTabs;
@property RNNBottomTabOptions* bottomTab;
@property RNNTopTabsOptions* topTabs;
@property RNNSideMenuOptions* sideMenu;
@property RNNOverlayOptions* overlay;
@property RNNAnimationsOptions* animations;
@property RNNStatusBarOptions* statusBar;
@property RNNLayoutOptions* layout;
@property RNNModalOptions* modal;
@property RNNPreviewOptions* preview;
@property RNNSplitViewOptions* splitView;
@end
```
### Options Resolution
Options merge in hierarchy:
1. Component's own options
2. Parent controller options (loop through chain)
3. Default options (from `setDefaultOptions`)
## Event Emission
### RNNEventEmitter
Sends events to JavaScript via RCTEventEmitter:
| Event | Method |
|-------|--------|
| Component lifecycle | `sendComponentWillAppear:`, `sendComponentDidAppear:`, `sendComponentDidDisappear:` |
| Button press | `sendOnNavigationButtonPressed:buttonId:` |
| Command completion | `sendOnNavigationCommandCompletion:commandId:` |
| Tab events | `sendBottomTabSelected:unselected:`, `sendBottomTabLongPressed:`, `sendBottomTabPressed:` |
| Modal events | `sendModalsDismissedEvent:numberOfModalsDismissed:`, `sendModalAttemptedToDismissEvent:` |
| Screen events | `sendScreenPoppedEvent:` |
| Search | `sendOnSearchBarUpdated:text:isFocused:`, `sendOnSearchBarCancelPressed:` |
| Preview | `sendOnPreviewCompleted:previewComponentId:` |
### RNNBridgeEventEmitter
Concrete implementation that sends `onAppLaunched` on initialization.
## Modal & Overlay Management
### RNNModalManager
- Tracks presented modals array
- Supports custom transition animations via ScreenAnimationController
- Handles presentationController delegate for adaptive presentation
### RNNOverlayManager
- Manages UIWindow instances for overlays
- Each overlay gets its own RNNOverlayWindow
- Maintains previous window reference for restoration
- Controls window level and accessibility
## Animation System
### ScreenAnimationController
Implements `UIViewControllerAnimatedTransitioning`:
- Custom push/pop/modal transitions
- Content transitions (RNNEnterExitAnimation)
- Element transitions (ElementTransitionOptions)
- Shared element transitions (SharedElementTransitionOptions)
### Element Animators
- `ElementAnimator` - Individual element animations
- `SharedElementAnimator` - Cross-screen shared elements
- Transition types: Alpha, Scale, Translation, Frame, Bounds, Color, CornerRadius
### RNNSetRootAnimator
Animates window root replacement using CABasicAnimation.
## Navigation Types
### Stack Navigation (RNNStackController)
- Subclass of UINavigationController
- Push/pop with custom animations
- Back button customization
- Uses StackControllerDelegate for bar delegate handling
```objc
// Extension methods
@interface UINavigationController (RNNCommands)
- (void)push:(UIViewController*)vc onTop:(UIViewController*)onTopVC
animated:(BOOL)animated completion:(void(^)(void))completion rejection:(RCTPromiseRejectBlock)rejection;
- (void)popAnimated:(BOOL)animated completion:(void(^)(NSString*))completion rejection:(RCTPromiseRejectBlock)rejection;
@end
```
### Bottom Tabs (RNNBottomTabsController)
- Subclass of UITabBarController
- Tab attachment modes: Together, OnSwitchToTab, AfterInitialTab
- Long-press gesture support
- Badge and dot indicator support
### Side Menu (RNNSideMenuViewController)
- Uses MMDrawerController (third-party)
- Center, left, right child containers
- Configurable open modes and widths
### Top Tabs (RNNTopTabsViewController)
- Custom horizontal tab implementation
- Not based on UITabBarController
- Manual content switching
### Split View (RNNSplitViewController)
- UISplitViewController subclass
- Master-detail for iPad
## External Components
### RNNExternalComponentStore
Registry for native (non-React) view controllers.
### RNNExternalViewController
Wraps native UIViewController for integration:
```objc
[ReactNativeNavigation registerExternalComponent:@"NativeScreen"
callback:^(NSDictionary *props, RCTBridge *bridge) {
return [[MyNativeViewController alloc] init];
}];
```
## Key Files Reference
| File | Purpose |
|------|---------|
| `RNNBridgeModule.h/mm` | Bridge entry point (legacy) |
| `TurboModules/RNNTurboModule.mm` | TurboModule entry point (new arch) |
| `TurboModules/RNNTurboCommandsHandler.mm` | TurboModule command routing |
| `RNNCommandsHandler.h/mm` | Command execution |
| `RNNLayoutManager.h/mm` | Controller tracking |
| `RNNViewControllerFactory.h/mm` | Controller creation |
| `RNNComponentViewController.h/mm` | React screen |
| `RNNStackController.h/mm` | Stack navigation |
| `RNNBottomTabsController.h/mm` | Tab navigation |
| `RNNNavigationOptions.h/mm` | Options model |
| `RNNBasePresenter.h/mm` | Presenter base |
| `TopBarPresenter.h/mm` | Top bar styling |
| `RNNReactView.h/mm` | React view wrapper |
| `ScreenAnimationController.h/mm` | Custom transitions |