@apexnative/navigation 1.0.0-alpha.3 → 1.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/android/CLAUDE.md +26 -9
- package/android/CORE.md +32 -17
- package/android/RECIPES.md +9 -3
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +25 -9
- package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +29 -36
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +33 -22
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +5 -4
- package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +39 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +10 -5
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +47 -46
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +34 -7
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -4
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +12 -12
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +4 -3
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +25 -17
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +5 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +45 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +25 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +8 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +8 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +61 -15
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +25 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +2 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +85 -29
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +5 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +14 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +56 -21
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/cli/doctor/checks/app.js +61 -13
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/migrate/native.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/mainApplication.js +134 -19
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/transitions.md +6 -7
- package/docs-md/containers/modals.md +2 -2
- package/docs-md/guides/cli.md +1 -2
- package/docs-md/help/glossary.md +1 -1
- package/docs-md/help/troubleshooting.md +22 -5
- package/docs-md/index.json +4 -0
- package/docs-md/migration/from-react-native-navigation.md +8 -2
- package/docs-md/screen-ui/back-and-gestures.md +24 -2
- package/docs-md/start/installation.md +4 -2
- package/ios/CLAUDE.md +17 -5
- package/ios/CORE.md +18 -13
- package/ios/Contract/ANNCommandAnswer.h +9 -2
- package/ios/Contract/ANNCommandAnswer.mm +28 -30
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNErrorHandler.h +1 -1
- package/ios/Core/ANNErrorHandler.mm +3 -1
- package/ios/Core/ANNEventEmitter.h +1 -1
- package/ios/Core/ANNEventEmitter.mm +2 -2
- package/ios/Core/ANNJavaScriptCalls.h +34 -0
- package/ios/Core/ANNJavaScriptCalls.mm +61 -0
- package/ios/Core/ANNNavigationState.h +7 -5
- package/ios/Core/ANNNavigationState.mm +8 -7
- package/ios/Core/ANNNodeHelper.h +32 -12
- package/ios/Core/ANNNodeHelper.mm +100 -3
- package/ios/Core/ANNRenderWait.h +15 -1
- package/ios/Core/ANNRenderWait.mm +119 -17
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +55 -14
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentViewController.h +8 -0
- package/ios/Layouts/Screen/ANNComponentViewController.mm +32 -5
- package/ios/Layouts/Screen/ANNScreenPlace.h +41 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +175 -0
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -2
- package/ios/Layouts/Stack/ANNPreviewPush.h +5 -5
- package/ios/Layouts/Stack/ANNPreviewPush.mm +85 -22
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.mm +26 -3
- package/ios/Layouts/Stack/ANNStackQueue.h +3 -1
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +13 -5
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +2 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +109 -7
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNUserChoice.h +57 -0
- package/ios/Options/ANNUserChoice.mm +44 -0
- package/ios/Options/Params/ANNImageSource.h +61 -0
- package/ios/Options/Params/ANNImageSource.mm +101 -0
- package/ios/Options/Params/ANNOptionImages.h +37 -0
- package/ios/Options/Params/ANNOptionImages.mm +284 -0
- package/ios/Options/Params/Image.h +11 -2
- package/ios/Options/Params/Image.mm +15 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +11 -5
- package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
- package/ios/React/Slots/ANNSlotContainers.h +14 -6
- package/ios/React/Slots/ANNSlotContainers.mm +75 -2
- package/ios/TopBar/ANNBarButtons.h +9 -5
- package/ios/TopBar/ANNBarButtons.mm +211 -63
- package/ios/TopBar/ANNIconButtonImage.h +1 -0
- package/ios/TopBar/ANNIconButtonImage.mm +34 -1
- package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
- package/ios/TopBar/ANNLargeTitlePresenter.mm +26 -3
- package/ios/TopBar/ANNSearchPresenter.mm +170 -19
- package/ios/TopBar/ANNTopBarPresenter.h +5 -0
- package/ios/TopBar/ANNTopBarPresenter.mm +60 -11
- package/ios/TurboModules/ANNTurboModule.mm +39 -21
- package/lib/module/ARCHITECTURE.md +18 -18
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/engine/commands.js +9 -7
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +13 -9
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/options.js +44 -11
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/places.js +52 -7
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/props.js +30 -1
- package/lib/module/engine/props.js.map +1 -1
- package/lib/module/engine/rememberedOptions.js +1 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +22 -12
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +5 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/navigation.d.ts +4 -1
- package/lib/typescript/api/navigation.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/options.d.ts +5 -2
- package/lib/typescript/engine/options.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +7 -2
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/props.d.ts +8 -1
- package/lib/typescript/engine/props.d.ts.map +1 -1
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +19 -10
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +23 -4
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +13 -5
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +9 -0
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/topBar.d.ts +22 -9
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +18 -18
- package/src/adapters/NativeANNTurboModule.ts +5 -2
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/events/navigationEvents.ts +1 -1
- package/src/api/hooks/useNavigation.ts +1 -1
- package/src/api/navigation.ts +4 -1
- package/src/engine/commands.ts +19 -7
- package/src/engine/layout.ts +16 -9
- package/src/engine/options.ts +39 -11
- package/src/engine/places.ts +65 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/rememberedOptions.ts +4 -1
- package/src/engine/screens.tsx +23 -13
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/Events.ts +19 -10
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/options/animations.ts +24 -4
- package/src/interfaces/options/buttons.ts +13 -5
- package/src/interfaces/options/stack.ts +9 -0
- package/src/interfaces/options/topBar.ts +22 -9
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
|
@@ -126,11 +126,13 @@ In an app as the React Native template makes it, the window shows **one React vi
|
|
|
126
126
|
|
|
127
127
|
| File | What setup does | Why |
|
|
128
128
|
| --- | --- | --- |
|
|
129
|
-
| `MainApplication.kt` | Extends `NavigationApplication
|
|
129
|
+
| `MainApplication.kt` | Extends `NavigationApplication`; keeps the template's `loadReactNative(this)` in `onCreate`, or adds it. | It keeps one React host for all screens, made after `loadReactNative(this)` loaded React Native. Native screens made by `codegen` are registered there, before JS opens them. |
|
|
130
130
|
| `MainActivity.kt` | Extends `NavigationActivity`. | The activity holds the containers of the navigation instead of one React view. It gets the back press, edge-to-edge and configuration changes first. |
|
|
131
131
|
| `AndroidManifest.xml` | Adds `android:enableOnBackInvokedCallback="true"` to `<application>`. | On the first screen the system plays its own animation back to the home screen as the user swipes back (predictive back). Android 16 turns it on for apps that target API 36; an app that targets less opts in here. A value the app set itself stays. See [Back to the home screen](../screen-ui/back-and-gestures#system-animation). |
|
|
132
132
|
| `styles.xml` | `AppTheme` inherits `Theme.ApexNavigation`. | The bars, tabs and search are Material 3 views and take their colors from the app's theme. This one is Material 3 in neutral grays, light and dark; change any color in the same file. A theme of the app's own stays as it is. See [Android's colors](../architecture/theme-and-context#android). |
|
|
133
133
|
|
|
134
|
+
The main activity keeps the template's `android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"`. Without `orientation`, `screenSize`, `smallestScreenSize`, `screenLayout` or `uiMode` Android recreates the activity on a rotation, a resize, a fold or a light/dark switch, and every screen goes with it; `doctor` warns about it under Config changes.
|
|
135
|
+
|
|
134
136
|
#### iOS files
|
|
135
137
|
|
|
136
138
|
| File | What setup does | Why |
|
|
@@ -213,7 +215,7 @@ export {};
|
|
|
213
215
|
|
|
214
216
|
| Group | What it checks |
|
|
215
217
|
| --- | --- |
|
|
216
|
-
| App | React Native and the New Architecture, the package, the files `setup` sets up, the entry point, React Native loaded twice, the status bar of the template, the generated code of native screens. Typed commands: `navigation-env.d.ts` there, pointing to the navigation, with its `context` line, and read by `tsconfig.json`. The splash screen made from its settings, with its logo inside the circle of Android's splash icon
|
|
218
|
+
| App | React Native and the New Architecture, the package, the files `setup` sets up, the entry point, React Native loaded twice, the status bar of the template, the generated code of native screens. Typed commands: `navigation-env.d.ts` there, pointing to the navigation, with its `context` line, and read by `tsconfig.json`. The splash screen made from its settings, with its logo inside the circle of Android's splash icon. The app icon made from its settings, and an iOS icon set with no image, which the App Store refuses. |
|
|
217
219
|
| Navigation | From the app's code. Screens: no name given twice in `screens` and the groups, and no file of the screens' folder with a `defineScreen` or `nativeScreen` that no file imports, a screen made and never added. Links: each prefix of `linking` declared on both platforms, the declarations no prefix has, a main activity that isn't `singleTask`. Screen names, in a JavaScript app only: a name that `push`, `replace`, `showModal` or `showInColumn` gives and no screen has, with its file and line. |
|
|
218
220
|
| (Android) | JDK 17 in use, the Android SDK, `minSdk`, how edge-to-edge works in the app. |
|
|
219
221
|
| (iOS) | Xcode 26 or newer, CocoaPods, the pods of the navigation and whether they are out of date, the iOS version of the Podfile. |
|
package/ios/CLAUDE.md
CHANGED
|
@@ -27,7 +27,7 @@ Swift is not used here: apps reach the core through the public headers (`Public/
|
|
|
27
27
|
- `Layouts/SplitView/`: The split view module, plus the core's `ANNSplitViewPresenter` and `ANNSplitViewCommands`.
|
|
28
28
|
- `Layouts/Arrangement/`: The pair (iOS 27.1): `ANNArrangementViewController`, `ANNArrangementState`.
|
|
29
29
|
- `TopBar/`: The bar of a screen: title, buttons, back button, large title, appearance, search, toolbar, edge effects.
|
|
30
|
-
- `Options/`, `Options/Params`: One `ANN<Group>Options` class per group, the field table, the value kinds and parsers (`Bool`, `Color`, `Text`, `Image`, …).
|
|
30
|
+
- `Options/`, `Options/Params`: One `ANN<Group>Options` class per group, the field table, the value kinds and parsers (`Bool`, `Color`, `Text`, `Image`, …), the images' cache and loader (`ANNOptionImages`).
|
|
31
31
|
- `Animations/`: The custom transition engine: plan, animator, wait, frame driver, rules; `Elements/` and `Interpolators/`.
|
|
32
32
|
- `React/`, `React/Slots/`: React views of screens and components, the surface host, the safe area view, the slot host and slot screens.
|
|
33
33
|
- `Public/ApexNavigation/`: The headers apps import: `ApexNavigation.h`, `ANNAppDelegate.h`, `ANNSceneDelegate.h`.
|
|
@@ -42,7 +42,7 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
42
42
|
- Launch, the app-facing API — `Public/ApexNavigation/`; `Core/`: `ApexNavigation.mm`, `ANNAppDelegate.mm`, `ANNSceneDelegate.mm`, `ApexNavigation+Internal.h`
|
|
43
43
|
- Root, splash, reload — `Layouts/Root/`: `ANNRootCoordinator`, `ANNSplashViewController`; `Core/`: `ANNReloadCoordinator`
|
|
44
44
|
- Screen: React root, slot, native screen — `Layouts/Screen/`: `ANNComponentViewController`; `React/`: `ANNReactView`, `ANNReactViewCreator`, `Slots/`; `Layouts/External/`
|
|
45
|
-
- Screen: insets, background, keyboard focus — `Layouts/Screen/`: `ANNScreenInsets`, `ANNScreenSafeArea`, `ANNScreenBars`, `ANNComponentPresenter` (background, status bar blur), `ANNComponentViewController` (key commands)
|
|
45
|
+
- Screen: insets, background, keyboard focus — `Layouts/Screen/`: `ANNScreenInsets`, `ANNScreenSafeArea`, `ANNScreenBars`, `ANNScreenPlace` (the safe area of a parked screen), `ANNComponentPresenter` (background, status bar blur), `ANNComponentViewController` (key commands)
|
|
46
46
|
- Safe area view — `React/`: `ANNSafeAreaViewComponentView`; `Layouts/Screen/`: `ANNScreenReadiness`; `cpp/`
|
|
47
47
|
- Stack: queue, commands, back, gestures — `Layouts/Stack/`: `ANNStackQueue`, `ANNStackCommands`, `ANNStackController`, `InteractivePopGestureDelegate`, `ANNStackAnimatingDelegate`
|
|
48
48
|
- Top bar: title, buttons, menus, large title — `TopBar/`: `ANNTopBarPresenter`, `ANNBarButtons`, `ANNBarAppearance`, `ANNLargeTitlePresenter`, `ANNBackButtonPresenter`, `ANNTitleView`, `ANNTitleGlassView`
|
|
@@ -60,24 +60,31 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
60
60
|
- System bars, orientation, direction — `Core/`: `ANNSystemUI`, `ANNSystemUIRules`, `ANNAppDirection`, `ANNNodePresenter` (the answers to UIKit)
|
|
61
61
|
- Events and state — `Core/`: `ANNEventEmitter`, `ANNEventContract` (names and keys, made by `scripts/contract.mjs`), `ANNAppearanceDispatcher`, `ANNScreenAppearance`, `ANNNavigationState`; `TurboModules/`: `ANNTurboEventEmitter`
|
|
62
62
|
- Options: parsing, merging, defaults — `Options/`: `ANNOptions`, `ANNOptionField`, `ANNOptionsReader`, `ANNDefaultOptions`, `ANNOptionsCache`; `Core/`: `ANNOptionsRepaint`
|
|
63
|
-
- Error texts — `Contract/`: `ANNErrorTexts` (made from `src/interfaces/contract/errors.json` by `scripts/contract.mjs`); `Core/`: `ANNErrorHandler`
|
|
63
|
+
- Error texts and answers — `Contract/`: `ANNErrorTexts` and `ANNCommandContract` (made from `src/interfaces/contract/errors.json` and `commands.json` by `scripts/contract.mjs`); `Core/`: `ANNErrorHandler`
|
|
64
64
|
- Links — `Core/`: `ANNDeepLinkQueue`; the `getLaunchLink` method of `ANNTurboModule`
|
|
65
65
|
- Key commands — `Core/`: `ANNKeyCommands`; `Layouts/Screen/`: `ANNComponentViewController`
|
|
66
66
|
- Hinge, regions — `Core/`: `ANNHinge`; `Layouts/Screen/`: `ANNScreenRegions`; `Layouts/Arrangement/`: `ANNArrangementState`
|
|
67
67
|
- Native screens — `Layouts/External/`: `ANNExternalViewController`, `ANNNativeScreenCalls`; `Layouts/Screen/ANNNativeScreenContext.mm`
|
|
68
|
-
- Slots — `React/Slots/`: `ANNSlotHost`, `ANNSlotContainers
|
|
68
|
+
- Slots — `React/Slots/`: `ANNSlotHost`, `ANNSlotContainers` (containers and slot views by slot id: a change goes
|
|
69
|
+
straight to the views of its id), `ANNSlotScreenView`, `ANNSlotComponentView`; `cpp/`
|
|
69
70
|
|
|
70
71
|
## Threads
|
|
71
72
|
|
|
72
73
|
- **Module queue:** the layout check (`ANNLayoutReader`) and the sync getters. The check reads only stores safe from
|
|
73
74
|
any thread (`ApexNavigation hasNativeScreenNamed:`, `ANNArrangementState canShowArrangements`).
|
|
74
75
|
- **Main queue:** everything else. A command body starts with `ANNAssertMainQueue()`. The options of a layout are parsed
|
|
75
|
-
here (`ANNLayoutTreeOptions
|
|
76
|
+
here (`ANNLayoutTreeOptions`; an image keeps only its source). `ANNEventEmitter` moves an event from another queue
|
|
76
77
|
to the main queue.
|
|
78
|
+
- **The images' queue** (`ANNOptionImages`, `Options/Params`): every option image decodes there, through one cache.
|
|
79
|
+
An element that shows an image asks for it when it applies and waits for it (the first frame needs it); only a tab
|
|
80
|
+
item's new icon on the bar that shows comes later (`ANNBottomTabsPresenter`), the last build of the item winning.
|
|
77
81
|
- **Sync getters** answer JS during a screen's first render: `getScreenInsets`, `getScreenRegions`,
|
|
78
82
|
`getArrangementState`, `getHinge`, `getStartInfo`. They read `ANNScreenInsets`, `ANNScreenRegions`,
|
|
79
83
|
`ANNArrangementState`, `ANNHinge lastValue`, written on the main queue and guarded by `@synchronized`.
|
|
80
84
|
`ANNReloadCoordinator.generation` and `ANNEventEmitter.isCurrent` are readable from any thread.
|
|
85
|
+
- **After the calls of JavaScript:** `ANNJavaScriptCalls afterCallsMadeSoFar:` runs a step on the main queue after
|
|
86
|
+
every call JavaScript made by then, by the calls' own way (the JavaScript thread, the module's queue, the main
|
|
87
|
+
queue). A push's wait ends through it, so the `mergeOptions` of its screen's first commit come first.
|
|
81
88
|
|
|
82
89
|
## Checks
|
|
83
90
|
|
|
@@ -92,6 +99,11 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
92
99
|
|
|
93
100
|
- Native code never touches React Native's views (their subviews, their layout, their frames): Fabric owns and recycles
|
|
94
101
|
them. Read them; what a transition writes (`alpha`, `transform`, a hidden flag) goes back on every path.
|
|
102
|
+
- A screen parked by a wait for its content (`ANNParking`) has no parent, and UIKit gives its view no safe area: it lays
|
|
103
|
+
out with the safe area of its place (`ANNScreenPlace`, measured on a navigation controller of the core's), or its
|
|
104
|
+
list would move as the push starts and UIKit would show the large title only after the transition. On iOS 26 its
|
|
105
|
+
large title also keeps a place of the core's for the push (`arrivesByPushOn:`). Check Showcase 1.4 (with a font
|
|
106
|
+
size), every section panel and 12.6 on push, pop and swipe after any change to waits, parking or the large title.
|
|
95
107
|
- Every change of a stack's children goes through its `ANNStackQueue`. The core's own calls to UIKit's navigation
|
|
96
108
|
come only from there; UIKit's own pops are followed (`uikitShows:`), not fought.
|
|
97
109
|
- Every UIKit present and dismiss in the main window is a step in `ANNPresentationGate`. UIKit drops a transition
|
package/ios/CORE.md
CHANGED
|
@@ -68,25 +68,29 @@ dispatcher.
|
|
|
68
68
|
`+[ANNErrorTexts nowhereToShow]` when there is no window, then runs the body in `@try`. The body finds Home in
|
|
69
69
|
`ANNNodeRegistry` and calls `+[ANNStackCommands push:componentId:what:answer:]`.
|
|
70
70
|
4. **`ANNStackCommands`.** Finds the stack by walking `layoutParent`; checks `ANNLayoutValidator` (an id already shown,
|
|
71
|
-
what a stack takes); reads the options of the whole tree with `ANNLayoutTreeOptions` (
|
|
72
|
-
|
|
73
|
-
goes back to that entry; else
|
|
71
|
+
what a stack takes); reads the options of the whole tree with `ANNLayoutTreeOptions` (an image keeps its source,
|
|
72
|
+
loaded through `ANNOptionImages` when an element shows it); checks `ANNLayoutFactory canMakeTrees:`; a node with
|
|
73
|
+
`preview.nativeID` goes to `ANNPreviewPush`; a `key` of an entry below goes back to that entry; else
|
|
74
|
+
`+[ANNLayoutFactory nodeFromLayout:options:]` makes Profile (an
|
|
74
75
|
`ANNComponentViewController` with an `ANNComponentPresenter`, content from `ANNReactViewCreator`) and registers it.
|
|
75
76
|
No view is rendered yet.
|
|
76
77
|
5. **`-[ANNStackQueue push:answer:…]`.** The node joins `entries` now (every check reads `entries`), the operation is
|
|
77
78
|
queued, `runNext` starts it when the queue is at rest.
|
|
78
79
|
6. **`startPush:` and `startPushTransition:`.** By `animations.push.waitForRender` the queue waits through
|
|
79
|
-
`ANNRenderWait
|
|
80
|
-
|
|
80
|
+
`ANNRenderWait`: unset, for the content at most 0.5 s, with `true` as long as it takes, then for the bar components
|
|
81
|
+
(their limits from the wait's start when unset, from the content's end with `true`; the screen is parked unseen in
|
|
82
|
+
the stack's view by `ANNParking`, `stackPark:`); the content's end, content there already too, waits for the calls
|
|
83
|
+
JavaScript made by then (`ANNJavaScriptCalls`: the JavaScript thread, the module's queue, the main queue), so the
|
|
84
|
+
options of the screen's first commit are in the bar; with `false` nothing. Then it renders the node, drives the appearance of Home and Profile (`+[ANNAppearanceDispatcher driveAppearanceOf:]`), and calls
|
|
81
85
|
`+[ANNAppearanceDispatcher beginTransitionShowing:]`. Profile's `ComponentWillAppear` is sent now (held until its
|
|
82
86
|
content mounted, 0.5 s at most). `stackChange:` pushes in UIKit; with an own animation `ANNTransitionPlan` and
|
|
83
87
|
`ANNStackAnimatingDelegate` give UIKit the `ANNTransitionAnimator`.
|
|
84
88
|
7. **The end.** `stackWhenTransitionEnds:` fires from the transition coordinator and calls `endPush:`, which calls
|
|
85
89
|
`+[ANNAppearanceDispatcher endTransitionHiding:showing:events:answer:value:]`. In this order: `ComponentDidDisappear`
|
|
86
|
-
of Home, `ComponentDidAppear` of Profile, `ANN.NavigationStateChanged` (
|
|
87
|
-
transition's `events`, then `-[ANNCommandAnswer resolve:]`: the
|
|
88
|
-
|
|
89
|
-
the next operation.
|
|
90
|
+
of Home, `ComponentDidAppear` of Profile, `ANN.NavigationStateChanged` (`-[ANNCommandAnswer reportStateNow]`, the
|
|
91
|
+
answer's one state step), the transition's `events`, then `-[ANNCommandAnswer resolve:]`: the promise with
|
|
92
|
+
Profile's id, then `ANN.CommandCompleted`. A command answered without a transition's end (`setRoot` with layout
|
|
93
|
+
modals) sends its state from `resolve:`. `finish:` frees the queue for the next operation.
|
|
90
94
|
|
|
91
95
|
Events go out through `ANNEventEmitter` on the main queue and reach JS through `ANNTurboEventEmitter`.
|
|
92
96
|
|
|
@@ -113,16 +117,17 @@ Each of these owns one rule. Call it; do not copy it.
|
|
|
113
117
|
|
|
114
118
|
- `ANNCommandRunner`: The one path of every async command: generation, check, main queue, exception guard. Called by: `ANNTurboModule`.
|
|
115
119
|
- `ANNLayoutReader`: The layout check, depth first, the same order and texts as Android. Module queue. Called by: The runner's check blocks.
|
|
116
|
-
- `ANNCommandAnswer`: The answer: state, then promise, then `ANN.CommandCompleted`; once; silent for a JavaScript that went. Called by: Every coordinator and queue.
|
|
120
|
+
- `ANNCommandAnswer`: The answer: state (one step per answer, `reportStateNow`, asked by the end of its transition or by `resolve:`), then promise, then `ANN.CommandCompleted`; once; silent for a JavaScript that went. Called by: Every coordinator and queue.
|
|
117
121
|
- `ANNNodeRegistry`: Every node by id, weakly. Filled by the factory, cleared at a reload. Called by: Commands, modules, `findViewController:`.
|
|
118
122
|
- `ANNLayoutFactory`: The core code that names module classes (`ANNSplitViewController`, `ANNSideMenuViewController`, `ANNArrangementViewController`); `ANNSideMenuPresenter` is the one other file that does. One maker per layout type; registers each node. Called by: Every command that shows a layout.
|
|
119
|
-
- `ANNOptionsCache`: The one invalidation point of resolved options. Called by: The
|
|
120
|
-
- `ANNNodeHelper resolveOptionsWithDefault`: What a node shows with: defaults, parents' own options, the node's `resolveOptions` (own, then the current child down). Cached, read-only. Called by: Presenters, coordinators.
|
|
123
|
+
- `ANNOptionsCache`: The one invalidation point of resolved options. Called by: The setters of `ANNNodeHelper.options` and of its user layer (`setUserChoice:forKey:`), and `ANNDefaultOptions`, only.
|
|
124
|
+
- `ANNNodeHelper resolveOptionsWithDefault`: What a node shows with: defaults, parents' own options (each with its user layer), the node's `resolveOptions` (own, its user layer, then the current child down). Cached, read-only. Called by: Presenters, coordinators.
|
|
121
125
|
- `ANNAppearanceDispatcher`: The only sender of `ComponentWillAppear`, `ComponentDidAppear`, `ComponentDidDisappear`; the end-of-transition order; the events of a node that goes. Called by: Screens, queues, coordinators.
|
|
122
|
-
- `ANNNavigationState`: The visible screen and the nearest tabs it is in (the last tabs met walking down); coalesced updates, holds, the state
|
|
126
|
+
- `ANNNavigationState`: The visible screen and the nearest tabs it is in (the last tabs met walking down); coalesced updates, holds, the state now. Called by: The dispatcher, `ANNCommandAnswer`, `ANNRootCoordinator`, `ANNModalCoordinator`, `ANNBottomTabsController`.
|
|
123
127
|
- `ANNScreenReadiness`: The signals of a screen's safe area view (has one, laid out, content rendered), sticky, by screen id. Called by: `ANNSafeAreaViewComponentView`, `ANNReactView`.
|
|
124
128
|
- `ANNRenderWait`: The one wait for the render of what a command shows: content, bar components, limits, the unknown-content rule. Called by: Root, stack queue, modals, overlays, `showInColumn`.
|
|
125
129
|
- `ANNParking`: Where a view waits for its content unseen: the window, a stack's view, a preview's host view. Called by: Root, stack, modals, overlays, `ANNPreviewPush`.
|
|
130
|
+
- `ANNScreenPlace`: The safe area a parked screen lays out with, measured where it will show on top of a stack. Called by: `ANNStackController` (`stackPark:`), `ANNSplitViewCommands` (`showInColumn`), through `placeWhileParked` of the screen.
|
|
126
131
|
- `ANNPendingLayers`: Layers that wait to show (a pending root, a modal, an overlay); a node inside counts as off screen. Called by: The layer's owner; nodes ask.
|
|
127
132
|
- `ANNShownScreens`: "What a node shows changed": tells the pending layer, the modal hold and the system UI, in that order. Called by: Every container that changes what it shows.
|
|
128
133
|
- `ANNModalCoordinator`, `ANNModal`: One record per modal, its states and waiting commands. Called by: Commands, root, state, repaint, reload.
|
|
@@ -27,8 +27,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
27
27
|
/// Whether the JavaScript the command came from still runs: its work goes on only then.
|
|
28
28
|
@property(nonatomic, readonly) BOOL isCurrent;
|
|
29
29
|
|
|
30
|
-
///
|
|
31
|
-
///
|
|
30
|
+
/// The navigation state now, sent when it changed, once per answer: the end of the command's transition asks for it
|
|
31
|
+
/// after the `DidAppear` it sends and before the transition's own events (`ANNAppearanceDispatcher`); otherwise
|
|
32
|
+
/// `resolve:` asks for it before the promise. A later call sends nothing, and nothing goes for a JavaScript that went.
|
|
33
|
+
/// A failure of the step is logged and costs only the state.
|
|
34
|
+
- (void)reportStateNow;
|
|
35
|
+
|
|
36
|
+
/// Resolves the promise: first the state, when the command changes what shows (`ANNCommandContract`) and its
|
|
37
|
+
/// transition didn't send it already (`reportStateNow`), then the promise, then `ANN.CommandCompleted` for a command
|
|
38
|
+
/// that shows or dismisses.
|
|
32
39
|
- (void)resolve:(nullable id)value;
|
|
33
40
|
|
|
34
41
|
/// Rejects the promise with the text alone: no code of the navigation.
|
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
#import "ANNCommandAnswer.h"
|
|
2
2
|
#import "ANNAssert.h"
|
|
3
|
+
#import "ANNCommandContract.h"
|
|
3
4
|
#import "ANNErrorHandler.h"
|
|
4
5
|
#import "ANNErrorTexts.h"
|
|
5
6
|
#import "ANNNavigationState.h"
|
|
6
7
|
#import "ANNReloadCoordinator.h"
|
|
7
8
|
#import "ApexNavigation+Internal.h"
|
|
8
9
|
|
|
9
|
-
// The commands that send `ANN.CommandCompleted` once they resolved: every one that shows or dismisses, not
|
|
10
|
-
// `setColumnShown`, `preparePush` or `getLaunchLink`.
|
|
11
|
-
static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
12
|
-
static NSSet<NSString *> *names;
|
|
13
|
-
static dispatch_once_t once;
|
|
14
|
-
dispatch_once(&once, ^{
|
|
15
|
-
names = [NSSet setWithArray:@[
|
|
16
|
-
@"setRoot", @"push", @"pop", @"popTo", @"popToRoot", @"setStackRoot", @"showInColumn",
|
|
17
|
-
@"showModal", @"dismissModal", @"dismissAllModals", @"showOverlay", @"dismissOverlay",
|
|
18
|
-
@"dismissAllOverlays"
|
|
19
|
-
]];
|
|
20
|
-
});
|
|
21
|
-
return names;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
10
|
@implementation ANNCommandAnswer {
|
|
25
11
|
RCTPromiseResolveBlock _resolve;
|
|
26
12
|
RCTPromiseRejectBlock _reject;
|
|
13
|
+
BOOL _stateReported;
|
|
27
14
|
}
|
|
28
15
|
|
|
29
16
|
- (instancetype)initWithCommandName:(NSString *)commandName
|
|
@@ -65,6 +52,27 @@ static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
|
65
52
|
return YES;
|
|
66
53
|
}
|
|
67
54
|
|
|
55
|
+
- (void)reportStateNow {
|
|
56
|
+
ANNAssertMainQueue();
|
|
57
|
+
if (_stateReported) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
_stateReported = YES;
|
|
61
|
+
// A step made for the JavaScript that went sends nothing, as its answer does.
|
|
62
|
+
if (!self.isCurrent) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// The command did its work: a failure of the state step costs only the state, never the events and the answer.
|
|
66
|
+
@try {
|
|
67
|
+
[ANNNavigationState updateNowFor:_commandName];
|
|
68
|
+
} @catch (NSException *exception) {
|
|
69
|
+
NSString *reason = exception.reason.length > 0 ? exception.reason : exception.name;
|
|
70
|
+
[ANNErrorHandler logFailureOf:_commandName
|
|
71
|
+
text:[ANNErrorTexts couldNot:@"report the navigation state"
|
|
72
|
+
reason:reason]];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
- (void)resolve:(id)value {
|
|
69
77
|
if (![self takeAnswer]) {
|
|
70
78
|
return;
|
|
@@ -72,23 +80,13 @@ static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
|
72
80
|
RCTPromiseResolveBlock resolve = _resolve;
|
|
73
81
|
_resolve = nil;
|
|
74
82
|
_reject = nil;
|
|
75
|
-
// A command that may change what shows (
|
|
76
|
-
// `getState()` after `await` has the result.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (changesWhatShows) {
|
|
80
|
-
// The command did its work: a failure of the state step costs only the state, never the answer.
|
|
81
|
-
@try {
|
|
82
|
-
[ANNNavigationState reportBeforeAnswer];
|
|
83
|
-
} @catch (NSException *exception) {
|
|
84
|
-
NSString *reason = exception.reason.length > 0 ? exception.reason : exception.name;
|
|
85
|
-
[ANNErrorHandler logFailureOf:_commandName
|
|
86
|
-
text:[ANNErrorTexts couldNot:@"report the navigation state"
|
|
87
|
-
reason:reason]];
|
|
88
|
-
}
|
|
83
|
+
// A command that may change what shows (`ANNCommandContract`): the state goes before the promise, so that
|
|
84
|
+
// `getState()` after `await` has the result. Sent at the end of its transition already, it goes no more.
|
|
85
|
+
if (ANNCommandReportsState(_commandName)) {
|
|
86
|
+
[self reportStateNow];
|
|
89
87
|
}
|
|
90
88
|
[ANNErrorHandler resolve:resolve of:_commandName value:value];
|
|
91
|
-
if (_commandId &&
|
|
89
|
+
if (_commandId && ANNCommandSendsCompleted(_commandName)) {
|
|
92
90
|
double completionTime = floor(NSDate.date.timeIntervalSince1970 * 1000);
|
|
93
91
|
[ApexNavigation.eventEmitter sendCommandCompleted:_commandName
|
|
94
92
|
commandId:_commandId
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Made by scripts/contract.mjs from src/interfaces/contract/commands.json: change that file and run the script again.
|
|
2
|
+
|
|
3
|
+
#import <Foundation/Foundation.h>
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
/// Whether the navigation state goes before the answer of the command `name`: every command that may change what
|
|
8
|
+
/// shows. Any thread.
|
|
9
|
+
FOUNDATION_EXPORT BOOL ANNCommandReportsState(NSString *name);
|
|
10
|
+
|
|
11
|
+
/// Whether `ANN.CommandCompleted` follows the answer of the command `name`: every command that shows or dismisses.
|
|
12
|
+
/// Any thread.
|
|
13
|
+
FOUNDATION_EXPORT BOOL ANNCommandSendsCompleted(NSString *name);
|
|
14
|
+
|
|
15
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Made by scripts/contract.mjs from src/interfaces/contract/commands.json: change that file and run the script again.
|
|
2
|
+
|
|
3
|
+
#import "ANNCommandContract.h"
|
|
4
|
+
|
|
5
|
+
// What each command resolves with:
|
|
6
|
+
// - `setRoot`: the id of the root, once the root and the layout's modals and overlays show.
|
|
7
|
+
// - `push`: the id of the screen that shows; of a pair, the pair's id.
|
|
8
|
+
// - `preparePush`: the frame `{ width, height, padding }` of the prepared screen, or null; always
|
|
9
|
+
// null on iOS.
|
|
10
|
+
// - `pop`: the id of the screen that went.
|
|
11
|
+
// - `popTo`: the id of the screen it went back to.
|
|
12
|
+
// - `popToRoot`: the id of the first screen of the stack.
|
|
13
|
+
// - `setStackRoot`: the id of the new top screen.
|
|
14
|
+
// - `showModal`: the id of the modal's root.
|
|
15
|
+
// - `dismissModal`: the id of the modal's root.
|
|
16
|
+
// - `dismissAllModals`: an empty string.
|
|
17
|
+
// - `showOverlay`: the id of the overlay's root.
|
|
18
|
+
// - `dismissOverlay`: the id of the overlay's root.
|
|
19
|
+
// - `dismissAllOverlays`: an empty string.
|
|
20
|
+
// - `showInColumn`: the id of the screen that shows.
|
|
21
|
+
// - `setColumnShown`: nothing once the columns stopped moving (`undefined` on iOS, `null` on
|
|
22
|
+
// Android).
|
|
23
|
+
// - `getLaunchLink`: the link that launched the app, or null.
|
|
24
|
+
|
|
25
|
+
static NSSet<NSString *> *ANNStateCommandNames(void) {
|
|
26
|
+
static NSSet<NSString *> *names;
|
|
27
|
+
static dispatch_once_t once;
|
|
28
|
+
dispatch_once(&once, ^{
|
|
29
|
+
names = [NSSet setWithArray:@[
|
|
30
|
+
@"setRoot",
|
|
31
|
+
@"push",
|
|
32
|
+
@"pop",
|
|
33
|
+
@"popTo",
|
|
34
|
+
@"popToRoot",
|
|
35
|
+
@"setStackRoot",
|
|
36
|
+
@"showModal",
|
|
37
|
+
@"dismissModal",
|
|
38
|
+
@"dismissAllModals",
|
|
39
|
+
@"showOverlay",
|
|
40
|
+
@"dismissOverlay",
|
|
41
|
+
@"dismissAllOverlays",
|
|
42
|
+
@"showInColumn",
|
|
43
|
+
@"setColumnShown",
|
|
44
|
+
]];
|
|
45
|
+
});
|
|
46
|
+
return names;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static NSSet<NSString *> *ANNCompletedCommandNames(void) {
|
|
50
|
+
static NSSet<NSString *> *names;
|
|
51
|
+
static dispatch_once_t once;
|
|
52
|
+
dispatch_once(&once, ^{
|
|
53
|
+
names = [NSSet setWithArray:@[
|
|
54
|
+
@"setRoot",
|
|
55
|
+
@"push",
|
|
56
|
+
@"pop",
|
|
57
|
+
@"popTo",
|
|
58
|
+
@"popToRoot",
|
|
59
|
+
@"setStackRoot",
|
|
60
|
+
@"showModal",
|
|
61
|
+
@"dismissModal",
|
|
62
|
+
@"dismissAllModals",
|
|
63
|
+
@"showOverlay",
|
|
64
|
+
@"dismissOverlay",
|
|
65
|
+
@"dismissAllOverlays",
|
|
66
|
+
@"showInColumn",
|
|
67
|
+
]];
|
|
68
|
+
});
|
|
69
|
+
return names;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
BOOL ANNCommandReportsState(NSString *name) {
|
|
73
|
+
return [ANNStateCommandNames() containsObject:name];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
BOOL ANNCommandSendsCompleted(NSString *name) {
|
|
77
|
+
return [ANNCompletedCommandNames() containsObject:name];
|
|
78
|
+
}
|
|
@@ -58,11 +58,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
58
58
|
|
|
59
59
|
/// A transition ended, in this order: first a screen a transition began inside a node of `shown` that left the window
|
|
60
60
|
/// and isn't one of that node's shown screens gets its `DidDisappear`; then `DidDisappear` of every screen of `hidden`,
|
|
61
|
-
/// `DidAppear` of what `shown` shows, the navigation state now when it changed
|
|
61
|
+
/// `DidAppear` of what `shown` shows, the navigation state now when it changed (the answer's one state step,
|
|
62
|
+
/// `-[ANNCommandAnswer reportStateNow]`; nothing for a JavaScript that went), `events` (the transition's own events,
|
|
62
63
|
/// `ANN.ScreenPopped` and `ANN.ModalDismissed`, and what must follow them, like stopping the content that went), then
|
|
63
|
-
/// `answer` resolves with `value` (
|
|
64
|
-
///
|
|
65
|
-
///
|
|
64
|
+
/// `answer` resolves with `value` (the promise, then `ANN.CommandCompleted`; no second state). A user's action passes
|
|
65
|
+
/// no answer, and its state goes all the same. The callbacks of UIKit that come later find the phases set and send
|
|
66
|
+
/// nothing.
|
|
66
67
|
+ (void)endTransitionHiding:(NSArray<UIViewController *> *)hidden
|
|
67
68
|
showing:(NSArray<UIViewController *> *)shown
|
|
68
69
|
events:(nullable dispatch_block_t)events
|
|
@@ -305,7 +305,18 @@ static BOOL ANNIsInsideNode(UIViewController *screen, UIViewController *node) {
|
|
|
305
305
|
for (UIViewController *node in shown) {
|
|
306
306
|
[self nodeDidAppear:node];
|
|
307
307
|
}
|
|
308
|
-
|
|
308
|
+
// The state before the transition's events, once per answer: the answer's own step before its promise finds it
|
|
309
|
+
// sent. A user's transition has no answer; one whose answer went before its end (an interrupt) sends the state of
|
|
310
|
+
// its end itself, as Android does.
|
|
311
|
+
if (!answer) {
|
|
312
|
+
[ANNNavigationState updateNow];
|
|
313
|
+
} else if (answer.answered) {
|
|
314
|
+
if (answer.isCurrent) {
|
|
315
|
+
[ANNNavigationState updateNowFor:answer.commandName];
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
[answer reportStateNow];
|
|
319
|
+
}
|
|
309
320
|
if (events) {
|
|
310
321
|
events();
|
|
311
322
|
}
|
|
@@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
14
14
|
/// Resolves a command's promise with `value`. Without a resolve block one line goes to the log instead.
|
|
15
15
|
+ (void)resolve:(nullable RCTPromiseResolveBlock)resolve of:(NSString *)method value:(nullable id)value;
|
|
16
16
|
|
|
17
|
-
/// The log line of a command without a promise: `[ApexNavigation] <method>: <text
|
|
17
|
+
/// The log line of a command without a promise: `[ApexNavigation] <method>: <text>`, a warning (`RCTLogWarn`).
|
|
18
18
|
+ (void)logFailureOf:(NSString *)method text:(NSString *)text;
|
|
19
19
|
|
|
20
20
|
@end
|
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
resolve(value);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// A warning, not an error: a failed command without a promise (a `mergeOptions` the screen can't take) is the app's
|
|
27
|
+
// to notice, not a red box over it; Android logs it the same way, without a dialog.
|
|
26
28
|
+ (void)logFailureOf:(NSString *)method text:(NSString *)text {
|
|
27
|
-
|
|
29
|
+
RCTLogWarn(@"[ApexNavigation] %@: %@", method, text);
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
@end
|
|
@@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
28
28
|
|
|
29
29
|
#pragma mark - The contract
|
|
30
30
|
|
|
31
|
-
/// `ANN.AppLaunched`,
|
|
31
|
+
/// `ANN.AppLaunched`, with an empty payload. While JavaScript has no listener of it, it is kept and goes once JavaScript
|
|
32
32
|
/// adds the first one. Main queue.
|
|
33
33
|
- (void)sendAppLaunched;
|
|
34
34
|
|
|
@@ -60,7 +60,7 @@ static NSUInteger ANNAppLaunchedBundle;
|
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
ANNAppLaunchedPending = NO;
|
|
63
|
-
[eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body
|
|
63
|
+
[eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body:@{}];
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
+ (void)eventEmitterListensToAppLaunched:(ANNTurboEventEmitter *)eventEmitter {
|
|
@@ -69,7 +69,7 @@ static NSUInteger ANNAppLaunchedBundle;
|
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
ANNAppLaunchedPending = NO;
|
|
72
|
-
[eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body
|
|
72
|
+
[eventEmitter sendIfListened:ANNEventContract.AppLaunched.event body:@{}];
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// Only an `AppLaunched` of the module's own JavaScript or an older one: the module is made while its bundle runs,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
|
|
3
|
+
@class RCTCallInvoker;
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
/// The way of the calls JavaScript makes to `ANNTurboModule`, for a step that must come after them. A call without an
|
|
8
|
+
/// answer (`mergeOptions`) leaves the JavaScript thread as it is made, goes to the module's queue (React Native's
|
|
9
|
+
/// `ModuleNativeMethodCallInvoker`, a `dispatch_async` to it) and from there to the main queue (`ANNCommandRunner`):
|
|
10
|
+
/// a step that takes the same three hops after them comes after them on the main queue, as each of the three runs its
|
|
11
|
+
/// work in order. The module tells its call invoker and its queue when React Native made it, and that it went when
|
|
12
|
+
/// React Native invalidates it (a reload). Any thread, except `afterCallsMadeSoFar:`.
|
|
13
|
+
@interface ANNJavaScriptCalls : NSObject
|
|
14
|
+
|
|
15
|
+
- (instancetype)init NS_UNAVAILABLE;
|
|
16
|
+
+ (instancetype)new NS_UNAVAILABLE;
|
|
17
|
+
|
|
18
|
+
/// The module of the JavaScript that runs now, its call invoker (the JavaScript thread) and its queue.
|
|
19
|
+
+ (void)setCallInvoker:(nullable RCTCallInvoker *)callInvoker
|
|
20
|
+
methodQueue:(nullable dispatch_queue_t)methodQueue
|
|
21
|
+
ofModule:(id)module;
|
|
22
|
+
|
|
23
|
+
/// The module went: with no newer module told since, no JavaScript runs to wait for.
|
|
24
|
+
+ (void)moduleWent:(id)module;
|
|
25
|
+
|
|
26
|
+
/// `block` runs on the main queue once every call JavaScript made to the module before the next task of its thread is
|
|
27
|
+
/// applied on the main queue: a task on the JavaScript thread, from there one on the module's queue, from there one on
|
|
28
|
+
/// the main queue. With no JavaScript to ask (none yet, a reload), at once. Main queue. A JavaScript thread that never
|
|
29
|
+
/// runs the task again (its runtime went) never runs `block`: a caller that can't wait for long has a limit of its own.
|
|
30
|
+
+ (void)afterCallsMadeSoFar:(dispatch_block_t)block;
|
|
31
|
+
|
|
32
|
+
@end
|
|
33
|
+
|
|
34
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#import "ANNJavaScriptCalls.h"
|
|
2
|
+
#import "ANNAssert.h"
|
|
3
|
+
#import <React/RCTCallInvoker.h>
|
|
4
|
+
#import <React/RCTUtils.h>
|
|
5
|
+
#import <ReactCommon/CallInvoker.h>
|
|
6
|
+
|
|
7
|
+
// The module told last, weakly: a module that went takes its invoker and its queue with it.
|
|
8
|
+
static __weak id ANNCallsModule;
|
|
9
|
+
static RCTCallInvoker *ANNCallsInvoker;
|
|
10
|
+
static dispatch_queue_t ANNCallsQueue;
|
|
11
|
+
|
|
12
|
+
@implementation ANNJavaScriptCalls
|
|
13
|
+
|
|
14
|
+
+ (void)setCallInvoker:(RCTCallInvoker *)callInvoker
|
|
15
|
+
methodQueue:(dispatch_queue_t)methodQueue
|
|
16
|
+
ofModule:(id)module {
|
|
17
|
+
@synchronized(self) {
|
|
18
|
+
ANNCallsModule = module;
|
|
19
|
+
ANNCallsInvoker = callInvoker;
|
|
20
|
+
ANNCallsQueue = methodQueue;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
+ (void)moduleWent:(id)module {
|
|
25
|
+
@synchronized(self) {
|
|
26
|
+
if (ANNCallsModule != module) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
ANNCallsModule = nil;
|
|
30
|
+
ANNCallsInvoker = nil;
|
|
31
|
+
ANNCallsQueue = nil;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
+ (void)afterCallsMadeSoFar:(dispatch_block_t)block {
|
|
36
|
+
ANNAssertMainQueue();
|
|
37
|
+
RCTCallInvoker *callInvoker = nil;
|
|
38
|
+
dispatch_queue_t queue = nil;
|
|
39
|
+
@synchronized(self) {
|
|
40
|
+
if (ANNCallsModule) {
|
|
41
|
+
callInvoker = ANNCallsInvoker;
|
|
42
|
+
queue = ANNCallsQueue;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
std::shared_ptr<facebook::react::CallInvoker> invoker =
|
|
46
|
+
callInvoker ? callInvoker.callInvoker : nullptr;
|
|
47
|
+
if (!invoker || !queue) {
|
|
48
|
+
block();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
dispatch_block_t done = [block copy];
|
|
52
|
+
// The JavaScript thread runs the task after the one it runs now, which made the calls of this moment (a commit of
|
|
53
|
+
// React and its layout effects); the module's queue then has those calls before this hop, and the main queue too.
|
|
54
|
+
invoker->invokeAsync([queue, done](facebook::jsi::Runtime &) {
|
|
55
|
+
dispatch_async(queue, ^{
|
|
56
|
+
RCTExecuteOnMainQueue(done);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@end
|
|
@@ -23,12 +23,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
23
23
|
+ (void)holdUpdates;
|
|
24
24
|
+ (void)releaseUpdates;
|
|
25
25
|
|
|
26
|
-
/// The state now, sent when it changed
|
|
27
|
-
///
|
|
28
|
-
|
|
26
|
+
/// The state now, sent when it changed. A command's answer asks for it once (`-[ANNCommandAnswer reportStateNow]`, at
|
|
27
|
+
/// the end of its transition or before its promise); the end of a user's transition and a tab chosen from code ask
|
|
28
|
+
/// `updateNow`. A walk that throws logs one line, `[ApexNavigation] <commandName>: could not report the navigation
|
|
29
|
+
/// state: <reason>` (as on Android; `transition` without a command), and sends nothing, so its caller always goes on.
|
|
30
|
+
+ (void)updateNowFor:(nullable NSString *)commandName;
|
|
29
31
|
|
|
30
|
-
///
|
|
31
|
-
+ (void)
|
|
32
|
+
/// `updateNowFor:` without a command.
|
|
33
|
+
+ (void)updateNow;
|
|
32
34
|
|
|
33
35
|
/// Forgets the last state sent: the next one goes even if it is the same (a new bundle, `AppLaunched`).
|
|
34
36
|
+ (void)reset;
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
#import "ANNAssert.h"
|
|
3
3
|
#import "ANNBottomTabsController.h"
|
|
4
4
|
#import "ANNComponentViewController.h"
|
|
5
|
+
#import "ANNErrorHandler.h"
|
|
5
6
|
#import "ANNErrorTexts.h"
|
|
6
7
|
#import "ANNModalCoordinator.h"
|
|
7
8
|
#import "ANNSlotContainers.h"
|
|
8
9
|
#import "ApexNavigation+Internal.h"
|
|
9
10
|
#import "UIViewController+LayoutProtocol.h"
|
|
10
|
-
#import <React/RCTLog.h>
|
|
11
11
|
|
|
12
12
|
// Main queue.
|
|
13
13
|
static BOOL ANNUpdateScheduled;
|
|
@@ -58,6 +58,10 @@ static BOOL ANNSameValue(id a, id b) {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
+ (void)updateNow {
|
|
61
|
+
[self updateNowFor:nil];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
+ (void)updateNowFor:(NSString *)commandName {
|
|
61
65
|
ANNAssertMainQueue();
|
|
62
66
|
NSString *screenId = nil;
|
|
63
67
|
NSString *name = nil;
|
|
@@ -74,8 +78,9 @@ static BOOL ANNSameValue(id a, id b) {
|
|
|
74
78
|
}
|
|
75
79
|
} @catch (NSException *exception) {
|
|
76
80
|
NSString *reason = exception.reason.length > 0 ? exception.reason : exception.name;
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
[ANNErrorHandler logFailureOf:commandName ?: @"transition"
|
|
82
|
+
text:[ANNErrorTexts couldNot:@"report the navigation state"
|
|
83
|
+
reason:reason]];
|
|
79
84
|
return;
|
|
80
85
|
}
|
|
81
86
|
if (ANNHasLastState && ANNSameValue(screenId, ANNLastScreenId) &&
|
|
@@ -89,10 +94,6 @@ static BOOL ANNSameValue(id a, id b) {
|
|
|
89
94
|
[ApexNavigation.eventEmitter sendNavigationStateChanged:screenId name:name tab:tab];
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
+ (void)reportBeforeAnswer {
|
|
93
|
-
[self updateNow];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
97
|
+ (void)reset {
|
|
97
98
|
ANNAssertMainQueue();
|
|
98
99
|
ANNHasLastState = NO;
|