@apexnative/navigation 1.0.0-alpha.4 → 1.0.0-alpha.6
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 +38 -0
- package/README.md +1 -1
- package/android/CLAUDE.md +23 -14
- package/android/CORE.md +32 -19
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +60 -13
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +28 -13
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +3 -3
- 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 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -51
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +30 -39
- 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 +12 -10
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +12 -12
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +22 -21
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +119 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +7 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- 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 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +3 -0
- 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 +2 -5
- 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/TopBarPresenter.kt +4 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +9 -2
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +11 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +318 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +18 -20
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +17 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +15 -22
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +84 -35
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +49 -40
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/splash/index.js +3 -1
- package/docs-md/architecture/transitions.md +4 -2
- package/docs-md/containers/modals.md +4 -2
- package/docs-md/containers/overlays.md +1 -1
- package/docs-md/containers/side-menu.md +1 -1
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/bottom-sheets.md +1 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/guides/cli.md +1 -2
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +23 -2
- package/docs-md/index.json +12 -0
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +1 -1
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +36 -0
- package/docs-md/screen-ui/bar-buttons.md +4 -4
- package/docs-md/screen-ui/search.md +1 -1
- package/docs-md/screen-ui/system-ui.md +16 -11
- package/docs-md/screen-ui/toolbar-and-fab.md +2 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/docs-md/start/installation.md +1 -1
- package/ios/Animations/ANNFrameDriver.mm +2 -0
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +70 -4
- package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
- package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
- package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
- package/ios/Animations/Interpolators/ANNViewTracks.h +6 -4
- package/ios/Animations/Interpolators/ANNViewTracks.mm +49 -9
- package/ios/CLAUDE.md +17 -15
- package/ios/CORE.md +30 -26
- package/ios/Contract/ANNCommandAnswer.h +9 -2
- package/ios/Contract/ANNCommandAnswer.mm +25 -12
- package/ios/Contract/ANNCommandRunner.h +8 -7
- package/ios/Contract/ANNCommandRunner.mm +21 -27
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNJavaScriptCalls.h +11 -12
- package/ios/Core/ANNJavaScriptCalls.mm +7 -16
- package/ios/Core/ANNNavigationState.h +4 -6
- package/ios/Core/ANNNavigationState.mm +0 -4
- package/ios/Core/ANNReloadCoordinator.mm +4 -8
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
- package/ios/Layouts/Screen/ANNComponentViewController.mm +9 -22
- package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
- package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
- package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
- package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
- package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
- package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
- package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
- package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
- package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
- package/ios/Options/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +18 -0
- package/ios/Options/ANNOptions.mm +112 -1
- package/ios/Options/ANNOptionsReader.mm +110 -0
- package/ios/Options/Params/ANNParam.h +15 -0
- package/ios/Options/Params/ANNParam.mm +61 -0
- package/ios/Options/Params/Color.h +3 -0
- package/ios/Options/Params/Color.mm +21 -0
- package/ios/Options/Params/Image.h +4 -0
- package/ios/Options/Params/Image.mm +20 -0
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
- package/ios/React/ANNSurfaceHostingView.h +6 -0
- package/ios/React/ANNSurfaceHostingView.mm +116 -0
- package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
- package/ios/TopBar/ANNBarAppearance.h +2 -1
- package/ios/TopBar/ANNBarAppearance.mm +111 -30
- package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
- package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
- package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
- package/ios/TopBar/ANNBarButtons.h +5 -3
- package/ios/TopBar/ANNBarButtons.mm +186 -17
- package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
- package/ios/TopBar/ANNMainList.h +3 -0
- package/ios/TopBar/ANNMainList.mm +4 -0
- package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
- package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
- package/ios/TopBar/ANNSearchPresenter.mm +2 -2
- package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
- package/ios/TopBar/ANNTopBarPresenter.h +3 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
- package/ios/TurboModules/ANNTurboModule.mm +20 -16
- package/lib/module/ARCHITECTURE.md +8 -4
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.js.map +1 -1
- package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- package/lib/module/engine/commands.js +21 -1
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/places.js +8 -0
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/screens.js +29 -4
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +3 -3
- 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/hooks/useSafeAreaInsets.d.ts +4 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
- package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/places.d.ts +2 -0
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +20 -6
- package/lib/typescript/engine/screens.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 +30 -9
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +18 -5
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +8 -8
- 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/system.d.ts +8 -5
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
- package/lib/typescript/interfaces/options/topBar.d.ts +32 -29
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +8 -4
- package/src/api/app/createNavigation.ts +3 -3
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/engine/commands.ts +30 -6
- package/src/engine/places.ts +9 -0
- package/src/engine/screens.tsx +29 -8
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/options/animations.ts +31 -9
- package/src/interfaces/options/bottomTabs.ts +18 -5
- package/src/interfaces/options/buttons.ts +8 -8
- package/src/interfaces/options/stack.ts +9 -0
- package/src/interfaces/options/system.ts +8 -5
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +32 -29
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
- 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
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
package/ios/CLAUDE.md
CHANGED
|
@@ -17,7 +17,7 @@ Swift is not used here: apps reach the core through the public headers (`Public/
|
|
|
17
17
|
- `Contract/`: The command path: runner, answer, layout reader and nodes, validator, error texts, tree options.
|
|
18
18
|
- `Core/`: The spine: node protocol and helper, registry, dispatcher, state, render wait, parking, system UI, reload, event emitter, start (`ApexNavigation.mm`, `ANNAppDelegate.mm`), and the modules `ANNKeyCommands`, `ANNDeepLinkQueue`, `ANNHinge`, `ANNSceneDelegate.mm`.
|
|
19
19
|
- `Layouts/Root/`: `ANNRootCoordinator` (`setRoot`), `ANNLayoutFactory`, `ANNSplashViewController`.
|
|
20
|
-
- `Layouts/Screen/`: The screen (`ANNComponentViewController`), its presenter, readiness,
|
|
20
|
+
- `Layouts/Screen/`: The screen (`ANNComponentViewController`), its presenter, readiness, its environment for JS (`ANNScreenEnvironment`), regions, safe-area math, the main list rule, previews.
|
|
21
21
|
- `Layouts/External/`: Native screens: `ANNExternalViewController`, `ANNNativeScreenCalls`.
|
|
22
22
|
- `Layouts/Stack/`: `ANNStackController`, `ANNStackQueue`, `ANNStackCommands`, `ANNStackPresenter`, `ANNPreviewPush`, the swipe-back delegate.
|
|
23
23
|
- `Layouts/Tabs/`: `ANNBottomTabsController`, its presenter, item builder, custom row, preload window, More delegate, long press, dots.
|
|
@@ -25,7 +25,7 @@ Swift is not used here: apps reach the core through the public headers (`Public/
|
|
|
25
25
|
- `Layouts/Overlay/`: `ANNOverlayCoordinator`, `ANNOverlayWindow`.
|
|
26
26
|
- `Layouts/SideMenu/`: The side menu module and its presenter and wrapper.
|
|
27
27
|
- `Layouts/SplitView/`: The split view module, plus the core's `ANNSplitViewPresenter` and `ANNSplitViewCommands`.
|
|
28
|
-
- `Layouts/Arrangement/`: The pair (iOS 27.1): `ANNArrangementViewController`, `ANNArrangementState
|
|
28
|
+
- `Layouts/Arrangement/`: The pair (iOS 27.1): `ANNArrangementViewController`, `ANNArrangementState` (whether pairs can show).
|
|
29
29
|
- `TopBar/`: The bar of a screen: title, buttons, back button, large title, appearance, search, toolbar, edge effects.
|
|
30
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/`.
|
|
@@ -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/`: `
|
|
45
|
+
- Screen: insets, background, keyboard focus — `Layouts/Screen/`: `ANNScreenEnvironment`, `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`
|
|
@@ -63,27 +63,29 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
63
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
|
-
- Hinge, regions — `Core/`: `ANNHinge`; `Layouts/Screen/`: `ANNScreenRegions
|
|
66
|
+
- Hinge, regions, arrangement — `Core/`: `ANNHinge`; `Layouts/Screen/`: `ANNScreenRegions` (the measuring), `ANNScreenEnvironment` (what JS reads); `Layouts/Arrangement/`: `ANNArrangementState`
|
|
67
67
|
- Native screens — `Layouts/External/`: `ANNExternalViewController`, `ANNNativeScreenCalls`; `Layouts/Screen/ANNNativeScreenContext.mm`
|
|
68
68
|
- Slots — `React/Slots/`: `ANNSlotHost`, `ANNSlotContainers` (containers and slot views by slot id: a change goes
|
|
69
69
|
straight to the views of its id), `ANNSlotScreenView`, `ANNSlotComponentView`; `cpp/`
|
|
70
70
|
|
|
71
71
|
## Threads
|
|
72
72
|
|
|
73
|
-
- **
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
here (`ANNLayoutTreeOptions`; an image keeps only its source). `ANNEventEmitter`
|
|
77
|
-
to the main queue.
|
|
73
|
+
- **Main queue:** everything but the sync getters and the images. `ANNTurboModule`'s method queue is the main queue,
|
|
74
|
+
as `ANNTurboEventEmitter`'s: an async method comes there from the JavaScript thread, and `ANNCommandRunner` runs the
|
|
75
|
+
layout check (`ANNLayoutReader`) and the body in that one task. A command body starts with `ANNAssertMainQueue()`.
|
|
76
|
+
The options of a layout are parsed here (`ANNLayoutTreeOptions`; an image keeps only its source). `ANNEventEmitter`
|
|
77
|
+
moves an event from another queue to the main queue.
|
|
78
78
|
- **The images' queue** (`ANNOptionImages`, `Options/Params`): every option image decodes there, through one cache.
|
|
79
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
80
|
item's new icon on the bar that shows comes later (`ANNBottomTabsPresenter`), the last build of the item winning.
|
|
81
|
-
- **Sync getters** answer JS
|
|
82
|
-
`
|
|
83
|
-
`
|
|
81
|
+
- **Sync getters** answer JS on the JavaScript thread (React Native calls them there whatever the module's queue)
|
|
82
|
+
during a screen's first render: `getScreenInsets`, `getScreenRegions`, `getArrangementState`, `getHinge`,
|
|
83
|
+
`getStartInfo`. They read `ANNScreenEnvironment` (one record per screen id: insets, padding, regions, arrangement)
|
|
84
|
+
and `ANNHinge lastValue`, written on the main queue and guarded by `@synchronized`. The environment's padding
|
|
85
|
+
listeners are main-queue only.
|
|
84
86
|
`ANNReloadCoordinator.generation` and `ANNEventEmitter.isCurrent` are readable from any thread.
|
|
85
87
|
- **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,
|
|
88
|
+
every call JavaScript made by then, by the calls' own way (a task on the JavaScript thread, then one on the main
|
|
87
89
|
queue). A push's wait ends through it, so the `mergeOptions` of its screen's first commit come first.
|
|
88
90
|
|
|
89
91
|
## Checks
|
|
@@ -91,8 +93,8 @@ Top-level files: `Package.swift` (SwiftPM; `headerFolders` lists every folder wh
|
|
|
91
93
|
- `scripts/build-ios.sh`: 0 warnings in `ios/` (warnings fail it). It builds the Showcase for the simulator.
|
|
92
94
|
- A new file needs `scripts/pods.sh` first (CocoaPods globs `ios/**`). A new folder also goes into `headerFolders` of
|
|
93
95
|
`Package.swift`, because files import each other's headers by bare name.
|
|
94
|
-
- There is no iOS unit-test target. `ANNSystemUIRulesSelfCheck` and `
|
|
95
|
-
rest is checked by the Showcase scenarios.
|
|
96
|
+
- There is no iOS unit-test target. `ANNSystemUIRulesSelfCheck`, `ANNCheckOptionClassesFrom` and `ANNCheckSameValues`
|
|
97
|
+
(the options' value equality) run once in DEBUG; the rest is checked by the Showcase scenarios.
|
|
96
98
|
- Format by hand: `.claude/rules/code-objc.md`.
|
|
97
99
|
|
|
98
100
|
## Pitfalls
|
package/ios/CORE.md
CHANGED
|
@@ -8,10 +8,10 @@ and files is `ios/CLAUDE.md`.
|
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
JS ── ANNTurboModule (codegen ApexNavigationSpec) ────────────────────────────────────────────┐ sync getters
|
|
11
|
-
│ module queue
|
|
11
|
+
│ main queue (the module's queue), one task per command, in call order │ read stores
|
|
12
12
|
▼ ▼ (@synchronized)
|
|
13
13
|
ANNCommandRunner ── ANNLayoutReader (check the whole layout) ── ANNErrorTexts ── ANNErrorHandler
|
|
14
|
-
│
|
|
14
|
+
│ the check passed: the body, in the same task
|
|
15
15
|
▼
|
|
16
16
|
area command (ANNStackCommands, ANNRootCoordinator, ANNModalCoordinator, ANNOverlayCoordinator, ANNSplitViewCommands)
|
|
17
17
|
│ ANNLayoutValidator · ANNLayoutTreeOptions · ANNLayoutFactory ── ANNNodeRegistry
|
|
@@ -32,12 +32,13 @@ JS ── ANNTurboModule (codegen ApexNavigationSpec) ────────
|
|
|
32
32
|
One line per layer. Each layer calls the next one down, and the answer and the events come back up through the
|
|
33
33
|
dispatcher.
|
|
34
34
|
|
|
35
|
-
- **Way in** (`ANNTurboModule`): the codegen methods.
|
|
36
|
-
|
|
35
|
+
- **Way in** (`ANNTurboModule`): the codegen methods. Its method queue is the main queue: an async method comes there
|
|
36
|
+
from the JavaScript thread and hands its work to `ANNCommandRunner`; a sync getter runs on the JavaScript thread,
|
|
37
|
+
reads a store and never touches the main queue.
|
|
37
38
|
- **Layout check** (`ANNLayoutReader`, `ANNLayoutNode`): reads the JSON into immutable nodes and checks the whole
|
|
38
39
|
tree before any view exists. The first error is the rejection text.
|
|
39
|
-
- **Command runner** (`ANNCommandRunner`, `ANNCommandAnswer`): takes the generation of the JavaScript, runs the check
|
|
40
|
-
the
|
|
40
|
+
- **Command runner** (`ANNCommandRunner`, `ANNCommandAnswer`): takes the generation of the JavaScript, runs the check,
|
|
41
|
+
then the body inside an exception guard, both in the command's one task on the main queue. The answer resolves or rejects once.
|
|
41
42
|
- **Registry and factory** (`ANNNodeRegistry`, `ANNLayoutFactory`): the factory turns a checked tree into view
|
|
42
43
|
controllers and registers every node by id.
|
|
43
44
|
- **Nodes** (`ANNLayoutProtocol`, `UIViewController+LayoutProtocol`, `ANNNodeHelper`): the view controllers. Each holds
|
|
@@ -58,20 +59,21 @@ dispatcher.
|
|
|
58
59
|
`navigation.push('Profile')` from Home: JS builds a layout node `{ id, type: 'Component', data, children }`, and calls
|
|
59
60
|
`push(commandId, componentId, layout)`.
|
|
60
61
|
|
|
61
|
-
1. **`ANNTurboModule push:componentId:layout:resolve:reject:`** (module queue
|
|
62
|
-
(`could not push the screen '…'`) and calls
|
|
63
|
-
|
|
62
|
+
1. **`ANNTurboModule push:componentId:layout:resolve:reject:`** (main queue, the module's queue: React Native sends
|
|
63
|
+
the call there from the JavaScript thread). Builds the failure text (`could not push the screen '…'`) and calls
|
|
64
|
+
`+[ANNCommandRunner run:…]` with a check block and a body block.
|
|
65
|
+
2. **`ANNCommandRunner`** (the same task). Takes `ANNReloadCoordinator.generation`. Runs the check:
|
|
64
66
|
`+[ANNLayoutReader nodeFrom:error:]` reads and validates the whole tree into `ANNLayoutNode`s. A failure rejects here,
|
|
65
|
-
through `ANNErrorHandler`, and nothing is
|
|
66
|
-
|
|
67
|
-
3. **Main queue.** The task does nothing when the JavaScript went (`answer.isCurrent`), rejects
|
|
67
|
+
through `ANNErrorHandler`, and nothing is made. Otherwise it makes the `ANNCommandAnswer`.
|
|
68
|
+
3. **The body.** The runner does nothing when the JavaScript went (`answer.isCurrent`), rejects
|
|
68
69
|
`+[ANNErrorTexts nowhereToShow]` when there is no window, then runs the body in `@try`. The body finds Home in
|
|
69
70
|
`ANNNodeRegistry` and calls `+[ANNStackCommands push:componentId:what:answer:]`.
|
|
70
71
|
4. **`ANNStackCommands`.** Finds the stack by walking `layoutParent`; checks `ANNLayoutValidator` (an id already shown,
|
|
71
72
|
what a stack takes); reads the options of the whole tree with `ANNLayoutTreeOptions` (an image keeps its source,
|
|
72
73
|
loaded through `ANNOptionImages` when an element shows it); checks `ANNLayoutFactory canMakeTrees:`; a node with
|
|
73
|
-
`preview.nativeID` goes to `ANNPreviewPush
|
|
74
|
-
|
|
74
|
+
`preview.nativeID` goes to `ANNPreviewPush` (it makes the node at once; a React screen with a root of its own
|
|
75
|
+
renders at the press, in the configuration call of `ANNPreviewInteraction`, other screens at once); a `key` of an
|
|
76
|
+
entry below goes back to that entry; else `+[ANNLayoutFactory nodeFromLayout:options:]` makes Profile (an
|
|
75
77
|
`ANNComponentViewController` with an `ANNComponentPresenter`, content from `ANNReactViewCreator`) and registers it.
|
|
76
78
|
No view is rendered yet.
|
|
77
79
|
5. **`-[ANNStackQueue push:answer:…]`.** The node joins `entries` now (every check reads `entries`), the operation is
|
|
@@ -80,17 +82,17 @@ dispatcher.
|
|
|
80
82
|
`ANNRenderWait`: unset, for the content at most 0.5 s, with `true` as long as it takes, then for the bar components
|
|
81
83
|
(their limits from the wait's start when unset, from the content's end with `true`; the screen is parked unseen in
|
|
82
84
|
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,
|
|
85
|
+
JavaScript made by then (`ANNJavaScriptCalls`: the JavaScript thread, then the main queue), so the
|
|
84
86
|
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
|
|
85
87
|
`+[ANNAppearanceDispatcher beginTransitionShowing:]`. Profile's `ComponentWillAppear` is sent now (held until its
|
|
86
88
|
content mounted, 0.5 s at most). `stackChange:` pushes in UIKit; with an own animation `ANNTransitionPlan` and
|
|
87
89
|
`ANNStackAnimatingDelegate` give UIKit the `ANNTransitionAnimator`.
|
|
88
90
|
7. **The end.** `stackWhenTransitionEnds:` fires from the transition coordinator and calls `endPush:`, which calls
|
|
89
91
|
`+[ANNAppearanceDispatcher endTransitionHiding:showing:events:answer:value:]`. In this order: `ComponentDidDisappear`
|
|
90
|
-
of Home, `ComponentDidAppear` of Profile, `ANN.NavigationStateChanged` (
|
|
91
|
-
transition's `events`, then `-[ANNCommandAnswer resolve:]`: the
|
|
92
|
-
|
|
93
|
-
the next operation.
|
|
92
|
+
of Home, `ComponentDidAppear` of Profile, `ANN.NavigationStateChanged` (`-[ANNCommandAnswer reportStateNow]`, the
|
|
93
|
+
answer's one state step), the transition's `events`, then `-[ANNCommandAnswer resolve:]`: the promise with
|
|
94
|
+
Profile's id, then `ANN.CommandCompleted`. A command answered without a transition's end (`setRoot` with layout
|
|
95
|
+
modals) sends its state from `resolve:`. `finish:` frees the queue for the next operation.
|
|
94
96
|
|
|
95
97
|
Events go out through `ANNEventEmitter` on the main queue and reach JS through `ANNTurboEventEmitter`.
|
|
96
98
|
|
|
@@ -115,15 +117,15 @@ How the others differ:
|
|
|
115
117
|
|
|
116
118
|
Each of these owns one rule. Call it; do not copy it.
|
|
117
119
|
|
|
118
|
-
- `ANNCommandRunner`: The one path of every async command: generation, check,
|
|
119
|
-
- `ANNLayoutReader`: The layout check, depth first, the same order and texts as Android.
|
|
120
|
-
- `ANNCommandAnswer`: The answer: state, then promise, then `ANN.CommandCompleted`; once; silent for a JavaScript that went. Called by: Every coordinator and queue.
|
|
120
|
+
- `ANNCommandRunner`: The one path of every async command: main queue (the module's queue), generation, check, exception guard. Called by: `ANNTurboModule`.
|
|
121
|
+
- `ANNLayoutReader`: The layout check, depth first, the same order and texts as Android. Main queue, before the body. Called by: The runner's check blocks.
|
|
122
|
+
- `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.
|
|
121
123
|
- `ANNNodeRegistry`: Every node by id, weakly. Filled by the factory, cleared at a reload. Called by: Commands, modules, `findViewController:`.
|
|
122
124
|
- `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.
|
|
123
125
|
- `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
126
|
- `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.
|
|
125
127
|
- `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.
|
|
126
|
-
- `ANNNavigationState`: The visible screen and the nearest tabs it is in (the last tabs met walking down); coalesced updates, holds, the state
|
|
128
|
+
- `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`.
|
|
127
129
|
- `ANNScreenReadiness`: The signals of a screen's safe area view (has one, laid out, content rendered), sticky, by screen id. Called by: `ANNSafeAreaViewComponentView`, `ANNReactView`.
|
|
128
130
|
- `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`.
|
|
129
131
|
- `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`.
|
|
@@ -150,7 +152,7 @@ the stores. By area:
|
|
|
150
152
|
`ANNArrangementViewController`; `UITabBar+utils`, `ANNBottomTabsAccessoryView`.
|
|
151
153
|
- **Modals:** `ANNSheetPresenter`, `ANNPopoverPresenter`, `ANNModalSizePresenter`, `ANNModalHostViewComponentView`;
|
|
152
154
|
`ANNOverlayWindow`.
|
|
153
|
-
- **Screen and bar:** `ANNPreviewInteraction`, `
|
|
155
|
+
- **Screen and bar:** `ANNPreviewInteraction`, `ANNScreenEnvironment`, `ANNScreenRegions`, `ANNNativeScreenContext`,
|
|
154
156
|
`ANNNativeScreenCalls`, `ANNSearchPresenter`, `ANNSearchResultsViewController`; options `ANNMenuOptions`,
|
|
155
157
|
`ANNToolbarOptions`, `ANNSheetOptions`, `ANNPopoverOptions`, `ANNBottomTabsAccessoryOptions`.
|
|
156
158
|
- **React and C++:** `React/Slots/`, `ANNSafeAreaViewComponentView`, the Fabric files of `cpp/`.
|
|
@@ -174,8 +176,10 @@ What a module uses of the core:
|
|
|
174
176
|
- **The presenter hooks** of `ANNNodePresenter`: `applyOptions:`, `mergeOptions:resolvedOptions:`,
|
|
175
177
|
`applyDefaultOptionsChange:`, `descendantWillAppear:`, `mergeChildOptions:`, `optionsForParent:`, and the answers to
|
|
176
178
|
UIKit (status bar, orientations, home indicator, deferred edges).
|
|
177
|
-
- **The stores** a module writes on the main queue and JS reads from any thread: `
|
|
178
|
-
|
|
179
|
+
- **The stores** a module writes on the main queue and JS reads from any thread: `ANNScreenEnvironment`, one record
|
|
180
|
+
per screen id (insets and padding, regions, arrangement) under one lock, cleared with one call when the screen goes
|
|
181
|
+
(`removeScreenId:`, only once no node holds the id) and at a reload (`removeAll`); and the signals
|
|
182
|
+
`ANNScreenReadiness`, `ANNSlotContainers`.
|
|
179
183
|
- **A module's options group** subclasses `ANNOptions` and overrides `initWithDict:`, `mergeOptions:` and `hasValue`.
|
|
180
184
|
|
|
181
185
|
Core code that lives in a module's folder: `ANNSplitViewPresenter` (turns `splitView.*` into calls of
|
|
@@ -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.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
@implementation ANNCommandAnswer {
|
|
11
11
|
RCTPromiseResolveBlock _resolve;
|
|
12
12
|
RCTPromiseRejectBlock _reject;
|
|
13
|
+
BOOL _stateReported;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
- (instancetype)initWithCommandName:(NSString *)commandName
|
|
@@ -51,6 +52,27 @@
|
|
|
51
52
|
return YES;
|
|
52
53
|
}
|
|
53
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
|
+
|
|
54
76
|
- (void)resolve:(id)value {
|
|
55
77
|
if (![self takeAnswer]) {
|
|
56
78
|
return;
|
|
@@ -59,18 +81,9 @@
|
|
|
59
81
|
_resolve = nil;
|
|
60
82
|
_reject = nil;
|
|
61
83
|
// A command that may change what shows (`ANNCommandContract`): the state goes before the promise, so that
|
|
62
|
-
// `getState()` after `await` has the result.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// The command did its work: a failure of the state step costs only the state, never the answer.
|
|
66
|
-
@try {
|
|
67
|
-
[ANNNavigationState reportBeforeAnswerOf:_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
|
-
}
|
|
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];
|
|
74
87
|
}
|
|
75
88
|
[ANNErrorHandler resolve:resolve of:_commandName value:value];
|
|
76
89
|
if (_commandId && ANNCommandSendsCompleted(_commandName)) {
|
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
NS_ASSUME_NONNULL_BEGIN
|
|
5
5
|
|
|
6
|
-
/// Reads and checks a command's layout
|
|
6
|
+
/// Reads and checks a command's layout before its body, in the same task: the layout read, or nil with the text of the
|
|
7
|
+
/// rejection.
|
|
7
8
|
typedef id _Nullable (^ANNCommandCheck)(NSString *_Nullable *_Nonnull error);
|
|
8
9
|
|
|
9
|
-
/// The command's own work
|
|
10
|
-
///
|
|
10
|
+
/// The command's own work, with the layout its check read (nil without a check). It answers once, now or later; an
|
|
11
|
+
/// exception it throws rejects the command, and undoing what it made by then is the body's.
|
|
11
12
|
typedef void (^ANNCommandBody)(id _Nullable layout, ANNCommandAnswer *answer);
|
|
12
13
|
|
|
13
|
-
/// The one path of every asynchronous method of `ANNTurboModule
|
|
14
|
-
///
|
|
15
|
-
///
|
|
14
|
+
/// The one path of every asynchronous method of `ANNTurboModule`, on the main queue (the module's queue), in one task
|
|
15
|
+
/// per command in the order JavaScript made them: the command takes the generation of its JavaScript and its layout is
|
|
16
|
+
/// read and checked whole, a failure rejecting it before anything is made; then the command does nothing when its
|
|
16
17
|
/// JavaScript went, rejects when the navigation has nowhere to show screens, and runs its body inside the protection.
|
|
17
18
|
@interface ANNCommandRunner : NSObject
|
|
18
19
|
|
|
@@ -27,7 +28,7 @@ typedef void (^ANNCommandBody)(id _Nullable layout, ANNCommandAnswer *answer);
|
|
|
27
28
|
check:(nullable ANNCommandCheck)check
|
|
28
29
|
body:(ANNCommandBody)body;
|
|
29
30
|
|
|
30
|
-
/// A command without a promise: its body
|
|
31
|
+
/// A command without a promise: its body at once, inside the protection; a failure is one log line. Main queue.
|
|
31
32
|
+ (void)runVoid:(NSString *)name what:(NSString *)what body:(dispatch_block_t)body;
|
|
32
33
|
|
|
33
34
|
/// The log line of a command without a promise that failed: `[ApexNavigation] <name>: <text>`.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#import "ANNCommandRunner.h"
|
|
2
|
+
#import "ANNAssert.h"
|
|
2
3
|
#import "ANNErrorHandler.h"
|
|
3
4
|
#import "ANNErrorTexts.h"
|
|
4
5
|
#import "ANNReloadCoordinator.h"
|
|
5
6
|
#import "ApexNavigation+Internal.h"
|
|
6
|
-
#import <React/RCTUtils.h>
|
|
7
7
|
|
|
8
8
|
// Why an exception happened: its message, else its name.
|
|
9
9
|
static NSString *ANNReasonOf(NSException *exception) {
|
|
@@ -27,6 +27,8 @@ static NSString *ANNRejectionOf(NSException *exception, NSString *what) {
|
|
|
27
27
|
reject:(RCTPromiseRejectBlock)reject
|
|
28
28
|
check:(ANNCommandCheck)check
|
|
29
29
|
body:(ANNCommandBody)body {
|
|
30
|
+
// The module's methods come on the main queue: the check and the body run in this one task.
|
|
31
|
+
ANNAssertMainQueue();
|
|
30
32
|
NSUInteger generation = ANNReloadCoordinator.generation;
|
|
31
33
|
id layout = nil;
|
|
32
34
|
if (check) {
|
|
@@ -46,36 +48,28 @@ static NSString *ANNRejectionOf(NSException *exception, NSString *what) {
|
|
|
46
48
|
generation:generation
|
|
47
49
|
resolve:resolve
|
|
48
50
|
reject:reject];
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
[answer reject:ANNRejectionOf(exception, what)];
|
|
62
|
-
}
|
|
63
|
-
});
|
|
51
|
+
if (!answer.isCurrent) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (commandId && !ApexNavigation.mainWindow) {
|
|
55
|
+
[answer reject:ANNErrorTexts.nowhereToShow];
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
@try {
|
|
59
|
+
body(layout, answer);
|
|
60
|
+
} @catch (NSException *exception) {
|
|
61
|
+
[answer reject:ANNRejectionOf(exception, what)];
|
|
62
|
+
}
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
// Before the navigation started a command without a promise has nothing to act on: its body finds nothing.
|
|
67
66
|
+ (void)runVoid:(NSString *)name what:(NSString *)what body:(dispatch_block_t)body {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
body();
|
|
75
|
-
} @catch (NSException *exception) {
|
|
76
|
-
[self logFailure:name text:[ANNErrorTexts couldNot:what reason:ANNReasonOf(exception)]];
|
|
77
|
-
}
|
|
78
|
-
});
|
|
67
|
+
ANNAssertMainQueue();
|
|
68
|
+
@try {
|
|
69
|
+
body();
|
|
70
|
+
} @catch (NSException *exception) {
|
|
71
|
+
[self logFailure:name text:[ANNErrorTexts couldNot:what reason:ANNReasonOf(exception)]];
|
|
72
|
+
}
|
|
79
73
|
}
|
|
80
74
|
|
|
81
75
|
+ (void)logFailure:(NSString *)name text:(NSString *)text {
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
NS_ASSUME_NONNULL_BEGIN
|
|
5
5
|
|
|
6
|
-
/// One node of a command's layout, as `ANNLayoutReader` read and checked it
|
|
7
|
-
/// the
|
|
8
|
-
/// kind the contract has, or nothing.
|
|
6
|
+
/// One node of a command's layout, as `ANNLayoutReader` read and checked it before the command's body. Immutable: made
|
|
7
|
+
/// once, read by the command and the steps it runs later. The data stays the JSON JS sent; the accessors give its
|
|
8
|
+
/// fields with the kind the contract has, or nothing.
|
|
9
9
|
@interface ANNLayoutNode : NSObject
|
|
10
10
|
|
|
11
11
|
/// Made by `ANNLayoutReader` only: the id as JS gave it, the type, the node's `data` (empty when missing) and its
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
NS_ASSUME_NONNULL_BEGIN
|
|
6
6
|
|
|
7
7
|
/// Reads the layout JSON of a command into `ANNLayoutNode`s and checks it whole, before any view is made. It runs on
|
|
8
|
-
/// the
|
|
9
|
-
/// found, and nothing
|
|
10
|
-
///
|
|
8
|
+
/// the main queue, in the command's task before its body: a failure rejects the command with the text of the first
|
|
9
|
+
/// error found, and nothing is made. The stores it reads are safe from any thread (the registered native screens,
|
|
10
|
+
/// whether the app shows pairs).
|
|
11
11
|
///
|
|
12
12
|
/// One pass over the tree, depth first, children in order, the same order on both platforms so that the first error
|
|
13
13
|
/// is the same text. At each node: a known `type` (`''` for a node that is no object or has no string type), an `id`
|
|
@@ -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
|
}
|
|
@@ -5,28 +5,27 @@
|
|
|
5
5
|
NS_ASSUME_NONNULL_BEGIN
|
|
6
6
|
|
|
7
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,
|
|
9
|
-
/// `ModuleNativeMethodCallInvoker`, a `dispatch_async` to it) and
|
|
10
|
-
///
|
|
11
|
-
///
|
|
12
|
-
/// React Native
|
|
8
|
+
/// answer (`mergeOptions`) leaves the JavaScript thread as it is made, as a task on the main queue, the module's queue
|
|
9
|
+
/// (React Native's `ModuleNativeMethodCallInvoker`, a `dispatch_async` to it), and runs whole in that task
|
|
10
|
+
/// (`ANNCommandRunner`). A step sent from the JavaScript thread to the main queue after them comes after them: the
|
|
11
|
+
/// JavaScript thread runs its tasks in order, and the main queue runs what it is given in the order it was given. The
|
|
12
|
+
/// module tells its call invoker when React Native made it, and that it went when React Native invalidates it (a
|
|
13
|
+
/// reload). Any thread, except `afterCallsMadeSoFar:`.
|
|
13
14
|
@interface ANNJavaScriptCalls : NSObject
|
|
14
15
|
|
|
15
16
|
- (instancetype)init NS_UNAVAILABLE;
|
|
16
17
|
+ (instancetype)new NS_UNAVAILABLE;
|
|
17
18
|
|
|
18
|
-
/// The module of the JavaScript that runs now
|
|
19
|
-
+ (void)setCallInvoker:(nullable RCTCallInvoker *)callInvoker
|
|
20
|
-
methodQueue:(nullable dispatch_queue_t)methodQueue
|
|
21
|
-
ofModule:(id)module;
|
|
19
|
+
/// The module of the JavaScript that runs now and its call invoker (the JavaScript thread).
|
|
20
|
+
+ (void)setCallInvoker:(nullable RCTCallInvoker *)callInvoker ofModule:(id)module;
|
|
22
21
|
|
|
23
22
|
/// The module went: with no newer module told since, no JavaScript runs to wait for.
|
|
24
23
|
+ (void)moduleWent:(id)module;
|
|
25
24
|
|
|
26
25
|
/// `block` runs on the main queue once every call JavaScript made to the module before the next task of its thread is
|
|
27
|
-
/// applied
|
|
28
|
-
///
|
|
29
|
-
///
|
|
26
|
+
/// applied: a task on the JavaScript thread, from there one on the main queue. With no JavaScript to ask (none yet, a
|
|
27
|
+
/// reload), at once. Main queue. A JavaScript thread that never runs the task again (its runtime went) never runs
|
|
28
|
+
/// `block`: a caller that can't wait for long has a limit of its own.
|
|
30
29
|
+ (void)afterCallsMadeSoFar:(dispatch_block_t)block;
|
|
31
30
|
|
|
32
31
|
@end
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
#import "ANNJavaScriptCalls.h"
|
|
2
2
|
#import "ANNAssert.h"
|
|
3
3
|
#import <React/RCTCallInvoker.h>
|
|
4
|
-
#import <React/RCTUtils.h>
|
|
5
4
|
#import <ReactCommon/CallInvoker.h>
|
|
6
5
|
|
|
7
|
-
// The module told last, weakly: a module that went takes its invoker
|
|
6
|
+
// The module told last, weakly: a module that went takes its invoker with it.
|
|
8
7
|
static __weak id ANNCallsModule;
|
|
9
8
|
static RCTCallInvoker *ANNCallsInvoker;
|
|
10
|
-
static dispatch_queue_t ANNCallsQueue;
|
|
11
9
|
|
|
12
10
|
@implementation ANNJavaScriptCalls
|
|
13
11
|
|
|
14
|
-
+ (void)setCallInvoker:(RCTCallInvoker *)callInvoker
|
|
15
|
-
methodQueue:(dispatch_queue_t)methodQueue
|
|
16
|
-
ofModule:(id)module {
|
|
12
|
+
+ (void)setCallInvoker:(RCTCallInvoker *)callInvoker ofModule:(id)module {
|
|
17
13
|
@synchronized(self) {
|
|
18
14
|
ANNCallsModule = module;
|
|
19
15
|
ANNCallsInvoker = callInvoker;
|
|
20
|
-
ANNCallsQueue = methodQueue;
|
|
21
16
|
}
|
|
22
17
|
}
|
|
23
18
|
|
|
@@ -28,33 +23,29 @@ static dispatch_queue_t ANNCallsQueue;
|
|
|
28
23
|
}
|
|
29
24
|
ANNCallsModule = nil;
|
|
30
25
|
ANNCallsInvoker = nil;
|
|
31
|
-
ANNCallsQueue = nil;
|
|
32
26
|
}
|
|
33
27
|
}
|
|
34
28
|
|
|
35
29
|
+ (void)afterCallsMadeSoFar:(dispatch_block_t)block {
|
|
36
30
|
ANNAssertMainQueue();
|
|
37
31
|
RCTCallInvoker *callInvoker = nil;
|
|
38
|
-
dispatch_queue_t queue = nil;
|
|
39
32
|
@synchronized(self) {
|
|
40
33
|
if (ANNCallsModule) {
|
|
41
34
|
callInvoker = ANNCallsInvoker;
|
|
42
|
-
queue = ANNCallsQueue;
|
|
43
35
|
}
|
|
44
36
|
}
|
|
45
37
|
std::shared_ptr<facebook::react::CallInvoker> invoker =
|
|
46
38
|
callInvoker ? callInvoker.callInvoker : nullptr;
|
|
47
|
-
if (!invoker
|
|
39
|
+
if (!invoker) {
|
|
48
40
|
block();
|
|
49
41
|
return;
|
|
50
42
|
}
|
|
51
43
|
dispatch_block_t done = [block copy];
|
|
52
44
|
// 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)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
});
|
|
45
|
+
// React and its layout effects). Each of those calls went to the main queue as it was made, so this hop, sent from
|
|
46
|
+
// the same thread later, reaches the main queue behind them (a serial queue, in the order it was given).
|
|
47
|
+
invoker->invokeAsync([done](facebook::jsi::Runtime &) {
|
|
48
|
+
dispatch_async(dispatch_get_main_queue(), done);
|
|
58
49
|
});
|
|
59
50
|
}
|
|
60
51
|
|
|
@@ -23,17 +23,15 @@ 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.
|
|
29
30
|
+ (void)updateNowFor:(nullable NSString *)commandName;
|
|
30
31
|
|
|
31
32
|
/// `updateNowFor:` without a command.
|
|
32
33
|
+ (void)updateNow;
|
|
33
34
|
|
|
34
|
-
/// The command `commandName` is about to answer: when what shows changed, the new state goes now, before the promise.
|
|
35
|
-
+ (void)reportBeforeAnswerOf:(NSString *)commandName;
|
|
36
|
-
|
|
37
35
|
/// Forgets the last state sent: the next one goes even if it is the same (a new bundle, `AppLaunched`).
|
|
38
36
|
+ (void)reset;
|
|
39
37
|
|
|
@@ -94,10 +94,6 @@ static BOOL ANNSameValue(id a, id b) {
|
|
|
94
94
|
[ApexNavigation.eventEmitter sendNavigationStateChanged:screenId name:name tab:tab];
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
+ (void)reportBeforeAnswerOf:(NSString *)commandName {
|
|
98
|
-
[self updateNowFor:commandName];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
97
|
+ (void)reset {
|
|
102
98
|
ANNAssertMainQueue();
|
|
103
99
|
ANNHasLastState = NO;
|