@apexnative/navigation 1.0.0-alpha.2 → 1.0.0-alpha.4

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.
Files changed (226) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/android/CLAUDE.md +38 -9
  3. package/android/CORE.md +11 -4
  4. package/android/RECIPES.md +9 -3
  5. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
  6. package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
  7. package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
  8. package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
  9. package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
  10. package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
  11. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +2 -2
  12. package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
  13. package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
  14. package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
  15. package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +2 -24
  16. package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
  17. package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +27 -19
  18. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +5 -4
  19. package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
  20. package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +17 -7
  21. package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
  22. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +4 -3
  23. package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
  24. package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
  25. package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
  26. package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
  27. package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
  28. package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
  29. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
  30. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
  31. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +92 -19
  32. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
  33. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +1 -1
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +79 -13
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
  40. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +29 -12
  41. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +22 -18
  42. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +4 -0
  43. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +45 -6
  44. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +35 -9
  45. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
  46. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
  47. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
  48. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -0
  49. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +5 -1
  50. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
  51. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +72 -16
  52. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
  53. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +28 -18
  54. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +164 -13
  55. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
  56. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +136 -40
  57. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
  58. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +107 -4
  59. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +7 -5
  60. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +3 -3
  61. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
  62. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +131 -9
  63. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
  64. package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
  65. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +4 -2
  66. package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +15 -0
  67. package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +137 -1
  68. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
  69. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +8 -1
  70. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
  71. package/cli/doctor/checks/app.js +61 -13
  72. package/cli/migrate/native.js +18 -0
  73. package/cli/setup/source.js +2 -1
  74. package/cli/setup/steps/mainApplication.js +134 -19
  75. package/docs-md/architecture/transitions.md +12 -7
  76. package/docs-md/containers/modals.md +4 -2
  77. package/docs-md/containers/overlays.md +3 -1
  78. package/docs-md/containers/side-menu.md +4 -2
  79. package/docs-md/containers/tabs.md +2 -0
  80. package/docs-md/flows/forms-in-a-modal.md +1 -1
  81. package/docs-md/help/glossary.md +2 -2
  82. package/docs-md/help/troubleshooting.md +20 -3
  83. package/docs-md/index.json +12 -0
  84. package/docs-md/migration/from-react-native-navigation.md +8 -2
  85. package/docs-md/screen-ui/back-and-gestures.md +8 -2
  86. package/docs-md/screen-ui/system-ui.md +1 -1
  87. package/docs-md/start/installation.md +3 -1
  88. package/ios/CLAUDE.md +17 -5
  89. package/ios/CORE.md +13 -8
  90. package/ios/Contract/ANNCommandAnswer.mm +5 -20
  91. package/ios/Contract/ANNCommandContract.h +15 -0
  92. package/ios/Contract/ANNCommandContract.mm +78 -0
  93. package/ios/Core/ANNAppearanceDispatcher.mm +1 -1
  94. package/ios/Core/ANNErrorHandler.h +1 -1
  95. package/ios/Core/ANNErrorHandler.mm +3 -1
  96. package/ios/Core/ANNEventEmitter.h +1 -1
  97. package/ios/Core/ANNEventEmitter.mm +2 -2
  98. package/ios/Core/ANNJavaScriptCalls.h +34 -0
  99. package/ios/Core/ANNJavaScriptCalls.mm +61 -0
  100. package/ios/Core/ANNNavigationState.h +7 -3
  101. package/ios/Core/ANNNavigationState.mm +10 -5
  102. package/ios/Core/ANNNodeHelper.h +32 -12
  103. package/ios/Core/ANNNodeHelper.mm +100 -3
  104. package/ios/Core/ANNRenderWait.h +15 -1
  105. package/ios/Core/ANNRenderWait.mm +119 -17
  106. package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
  107. package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
  108. package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
  109. package/ios/Layouts/Modal/ANNModalCoordinator.mm +55 -14
  110. package/ios/Layouts/Screen/ANNComponentViewController.h +8 -0
  111. package/ios/Layouts/Screen/ANNComponentViewController.mm +32 -5
  112. package/ios/Layouts/Screen/ANNScreenPlace.h +41 -0
  113. package/ios/Layouts/Screen/ANNScreenPlace.mm +175 -0
  114. package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
  115. package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
  116. package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -2
  117. package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
  118. package/ios/Layouts/Stack/ANNStackController.mm +26 -3
  119. package/ios/Layouts/Stack/ANNStackQueue.h +3 -1
  120. package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
  121. package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
  122. package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
  123. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
  124. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
  125. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +13 -5
  126. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +2 -2
  127. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +109 -7
  128. package/ios/Options/ANNOptionsCache.h +7 -6
  129. package/ios/Options/ANNSearchBarOptions.h +4 -3
  130. package/ios/Options/ANNUserChoice.h +57 -0
  131. package/ios/Options/ANNUserChoice.mm +44 -0
  132. package/ios/Options/Params/ANNImageSource.h +61 -0
  133. package/ios/Options/Params/ANNImageSource.mm +101 -0
  134. package/ios/Options/Params/ANNOptionImages.h +37 -0
  135. package/ios/Options/Params/ANNOptionImages.mm +284 -0
  136. package/ios/Options/Params/Image.h +11 -2
  137. package/ios/Options/Params/Image.mm +15 -2
  138. package/ios/Options/Params/ImageParser.h +5 -5
  139. package/ios/Options/Params/ImageParser.mm +28 -46
  140. package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
  141. package/ios/RECIPES.md +11 -5
  142. package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
  143. package/ios/React/Slots/ANNSlotContainers.h +14 -6
  144. package/ios/React/Slots/ANNSlotContainers.mm +75 -2
  145. package/ios/TopBar/ANNBarButtons.h +9 -5
  146. package/ios/TopBar/ANNBarButtons.mm +211 -63
  147. package/ios/TopBar/ANNIconButtonImage.h +1 -0
  148. package/ios/TopBar/ANNIconButtonImage.mm +34 -1
  149. package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
  150. package/ios/TopBar/ANNLargeTitlePresenter.mm +26 -3
  151. package/ios/TopBar/ANNSearchPresenter.mm +170 -19
  152. package/ios/TopBar/ANNTopBarPresenter.h +5 -0
  153. package/ios/TopBar/ANNTopBarPresenter.mm +60 -11
  154. package/ios/TurboModules/ANNTurboModule.mm +39 -21
  155. package/lib/module/ARCHITECTURE.md +18 -18
  156. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  157. package/lib/module/api/hooks/useNavigation.js +8 -3
  158. package/lib/module/api/hooks/useNavigation.js.map +1 -1
  159. package/lib/module/api/navigation.js.map +1 -1
  160. package/lib/module/engine/commands.js +9 -7
  161. package/lib/module/engine/commands.js.map +1 -1
  162. package/lib/module/engine/layout.js +13 -9
  163. package/lib/module/engine/layout.js.map +1 -1
  164. package/lib/module/engine/options.js +44 -11
  165. package/lib/module/engine/options.js.map +1 -1
  166. package/lib/module/engine/places.js +44 -7
  167. package/lib/module/engine/places.js.map +1 -1
  168. package/lib/module/engine/props.js +30 -1
  169. package/lib/module/engine/props.js.map +1 -1
  170. package/lib/module/engine/rememberedOptions.js +1 -1
  171. package/lib/module/engine/rememberedOptions.js.map +1 -1
  172. package/lib/module/engine/screens.js +22 -12
  173. package/lib/module/engine/screens.js.map +1 -1
  174. package/lib/module/interfaces/contract/commands.json +101 -0
  175. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +5 -2
  176. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  177. package/lib/typescript/api/events/navigationEvents.d.ts +1 -1
  178. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  179. package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
  180. package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
  181. package/lib/typescript/api/navigation.d.ts +4 -1
  182. package/lib/typescript/api/navigation.d.ts.map +1 -1
  183. package/lib/typescript/engine/commands.d.ts.map +1 -1
  184. package/lib/typescript/engine/options.d.ts +5 -2
  185. package/lib/typescript/engine/options.d.ts.map +1 -1
  186. package/lib/typescript/engine/places.d.ts +5 -2
  187. package/lib/typescript/engine/places.d.ts.map +1 -1
  188. package/lib/typescript/engine/props.d.ts +8 -1
  189. package/lib/typescript/engine/props.d.ts.map +1 -1
  190. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  191. package/lib/typescript/engine/screens.d.ts.map +1 -1
  192. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  193. package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
  194. package/lib/typescript/interfaces/Events.d.ts +19 -10
  195. package/lib/typescript/interfaces/Events.d.ts.map +1 -1
  196. package/lib/typescript/interfaces/options/animations.d.ts +15 -7
  197. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  198. package/lib/typescript/interfaces/options/buttons.d.ts +13 -5
  199. package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
  200. package/lib/typescript/interfaces/options/modals.d.ts +18 -13
  201. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  202. package/lib/typescript/interfaces/options/system.d.ts +6 -3
  203. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  204. package/lib/typescript/interfaces/options/topBar.d.ts +22 -9
  205. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  206. package/package.json +1 -1
  207. package/src/ARCHITECTURE.md +18 -18
  208. package/src/adapters/NativeANNTurboModule.ts +5 -2
  209. package/src/api/events/navigationEvents.ts +1 -1
  210. package/src/api/hooks/useNavigation.ts +9 -4
  211. package/src/api/navigation.ts +4 -1
  212. package/src/engine/commands.ts +19 -7
  213. package/src/engine/layout.ts +16 -9
  214. package/src/engine/options.ts +39 -11
  215. package/src/engine/places.ts +56 -7
  216. package/src/engine/props.ts +30 -1
  217. package/src/engine/rememberedOptions.ts +4 -1
  218. package/src/engine/screens.tsx +23 -13
  219. package/src/interfaces/ComponentEvents.ts +2 -2
  220. package/src/interfaces/Events.ts +19 -10
  221. package/src/interfaces/contract/commands.json +101 -0
  222. package/src/interfaces/options/animations.ts +15 -7
  223. package/src/interfaces/options/buttons.ts +13 -5
  224. package/src/interfaces/options/modals.ts +18 -13
  225. package/src/interfaces/options/system.ts +6 -3
  226. package/src/interfaces/options/topBar.ts +22 -9
@@ -2,6 +2,7 @@
2
2
  #import "ANNEventEmitter.h"
3
3
  #import "ANNScreenInsets.h"
4
4
  #import "ANNSearchResultsViewController.h"
5
+ #import "ANNUserChoice.h"
5
6
 
6
7
  static void *ANNSearchFieldColorContext = &ANNSearchFieldColorContext;
7
8
 
@@ -49,6 +50,47 @@ static NSString *ANNResultsKey(ANNComponentOptions *component) {
49
50
  return [NSString stringWithFormat:@"%@\n%@", component.name.get, component.componentId.get];
50
51
  }
51
52
 
53
+ static NSString *const ANNScopeChoiceKey = @"topBar.searchBar.selectedScope";
54
+ static NSString *const ANNTokensChoiceKey = @"topBar.searchBar.tokens";
55
+ static NSString *const ANNSuggestionsChoiceKey = @"topBar.searchBar.suggestions";
56
+
57
+ static BOOL ANNHasScope(ANNNavigationOptions *options, NSString *scopeId) {
58
+ for (ANNSearchScopeOptions *scope in options.topBar.searchBar.scopes) {
59
+ if ([[scope.identifier withDefault:nil] isEqualToString:scopeId]) {
60
+ return YES;
61
+ }
62
+ }
63
+ return NO;
64
+ }
65
+
66
+ // The scope the user picked, by its id: it stays over the app's `selectedScope` where the scopes have it, as on
67
+ // Android (`UserChoices.yieldTo`). It goes at a merge into the screen that gives another `selectedScope` than the app
68
+ // had there (the own options', else the screen's resolution before the merge), or after which the screen's scopes
69
+ // don't have it.
70
+ static ANNUserChoice *ANNScopeChoice(NSString *scope) {
71
+ return [ANNUserChoice choiceWithValue:scope
72
+ apply:^(ANNNavigationOptions *options, id value) {
73
+ if (ANNHasScope(options, value)) {
74
+ options.topBar.searchBar.selectedScope = [Text withValue:value];
75
+ }
76
+ }
77
+ merged:^id(id value, ANNNavigationOptions *merge, ANNNavigationOptions *own,
78
+ ANNNavigationOptions *before, ANNNavigationOptions *after) {
79
+ if (!ANNHasScope(after, value)) {
80
+ return nil;
81
+ }
82
+ Text *given = merge.topBar.searchBar.selectedScope;
83
+ if (!given.hasValue) {
84
+ return value;
85
+ }
86
+ Text *ownScope = own.topBar.searchBar.selectedScope;
87
+ Text *was = ownScope.hasValue ? ownScope : before.topBar.searchBar.selectedScope;
88
+ NSString *merged = [given withDefault:nil];
89
+ NSString *had = [was withDefault:nil];
90
+ return merged == had || [merged isEqualToString:had] ? value : nil;
91
+ }];
92
+ }
93
+
52
94
  @interface ANNSearchPresenter () <UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate>
53
95
  @end
54
96
 
@@ -380,9 +422,11 @@ static NSString *ANNResultsKey(ANNComponentOptions *component) {
380
422
 
381
423
  #pragma mark - Tokens
382
424
 
425
+ // A token's key names its icon by the source the options give, never by the decoded image: a key read before and after
426
+ // the cache lets the image go, or a file at the same path changes, stays the same, and computing it loads nothing.
383
427
  static NSString *ANNTokenKey(ANNSearchTokenOptions *token) {
384
428
  return [NSString stringWithFormat:@"%@\n%@\n%@\n%@", [token.identifier withDefault:@""], [token.text withDefault:@""],
385
- [token.sfSymbol withDefault:@""], [token.icon withDefault:nil] ?: @""];
429
+ [token.sfSymbol withDefault:@""], token.icon.source.sourceKey ?: @""];
386
430
  }
387
431
 
388
432
  static NSArray<NSString *> *ANNTokenIds(NSArray<UISearchToken *> *tokens) {
@@ -395,6 +439,59 @@ static NSArray<NSString *> *ANNTokenIds(NSArray<UISearchToken *> *tokens) {
395
439
  return ids;
396
440
  }
397
441
 
442
+ static NSArray<NSString *> *ANNTokenKeys(NSArray<ANNSearchTokenOptions *> *tokens) {
443
+ NSMutableArray<NSString *> *keys = [NSMutableArray new];
444
+ for (ANNSearchTokenOptions *token in tokens) {
445
+ [keys addObject:ANNTokenKey(token)];
446
+ }
447
+ return keys;
448
+ }
449
+
450
+ // The tokens the field has after the user deleted some (or brought them back with Undo), as copies of their options:
451
+ // they stay over the app's `tokens` until a merge gives a list other than the app had there (the own options', else
452
+ // the screen's resolution before the merge), as the other choices. After a merge the tokens the screen's resolution no
453
+ // longer has (by id) leave the choice, and all of them when it has no `tokens`.
454
+ static ANNUserChoice *ANNTokensChoice(NSArray<ANNSearchTokenOptions *> *tokens) {
455
+ return [ANNUserChoice choiceWithValue:tokens
456
+ apply:^(ANNNavigationOptions *options, id value) {
457
+ // Copies: two trees never share a group.
458
+ NSArray<ANNSearchTokenOptions *> *kept = value;
459
+ NSMutableArray<ANNSearchTokenOptions *> *copies =
460
+ [NSMutableArray arrayWithCapacity:kept.count];
461
+ for (ANNSearchTokenOptions *token in kept) {
462
+ [copies addObject:[token copy]];
463
+ }
464
+ options.topBar.searchBar.tokens = copies;
465
+ }
466
+ merged:^id(id value, ANNNavigationOptions *merge, ANNNavigationOptions *own,
467
+ ANNNavigationOptions *before, ANNNavigationOptions *after) {
468
+ NSArray<ANNSearchTokenOptions *> *now = after.topBar.searchBar.tokens;
469
+ if (!now) {
470
+ return nil;
471
+ }
472
+ NSArray<ANNSearchTokenOptions *> *given = merge.topBar.searchBar.tokens;
473
+ if (given) {
474
+ NSArray<ANNSearchTokenOptions *> *was =
475
+ own.topBar.searchBar.tokens ?: before.topBar.searchBar.tokens;
476
+ if (!was || ![ANNTokenKeys(given) isEqualToArray:ANNTokenKeys(was)]) {
477
+ return nil;
478
+ }
479
+ }
480
+ NSMutableSet<NSString *> *ids = [NSMutableSet new];
481
+ for (ANNSearchTokenOptions *token in now) {
482
+ [ids addObject:[token.identifier withDefault:@""]];
483
+ }
484
+ NSArray<ANNSearchTokenOptions *> *chosen = value;
485
+ NSMutableArray<ANNSearchTokenOptions *> *kept = [NSMutableArray new];
486
+ for (ANNSearchTokenOptions *token in chosen) {
487
+ if ([ids containsObject:[token.identifier withDefault:@""]]) {
488
+ [kept addObject:token];
489
+ }
490
+ }
491
+ return kept.count == chosen.count ? value : kept;
492
+ }];
493
+ }
494
+
398
495
  // The field gets the tokens again only when the options changed: setting them resets the user's selection.
399
496
  - (void)applyTokens:(NSArray<ANNSearchTokenOptions *> *)tokens field:(UISearchTextField *)field {
400
497
  if (!tokens) {
@@ -426,7 +523,7 @@ static NSArray<NSString *> *ANNTokenIds(NSArray<UISearchToken *> *tokens) {
426
523
  field.tokens = searchTokens;
427
524
  }
428
525
 
429
- // The tokens the field has now, written to the screen's options when the user deleted some (or brought them back with
526
+ // The tokens the field has now, kept in the screen's user layer when the user deleted some (or brought them back with
430
527
  // Undo), so the options apply them as they are. Their ids, or nil when the search bar has no tokens option.
431
528
  - (NSArray<NSString *> *)syncTokens:(UISearchTextField *)field {
432
529
  if (!_tokenOptions) {
@@ -444,24 +541,73 @@ static NSArray<NSString *> *ANNTokenIds(NSArray<UISearchToken *> *tokens) {
444
541
  }
445
542
  if (![_appliedTokens isEqualToArray:keys]) {
446
543
  _appliedTokens = keys;
447
- [_viewController changeOwnOptions:^(ANNNavigationOptions *options) {
448
- // Copies: the objects above belong to the applied tree, and two trees never share a group.
449
- NSMutableArray<ANNSearchTokenOptions *> *own = [NSMutableArray arrayWithCapacity:tokens.count];
450
- for (ANNSearchTokenOptions *token in tokens) {
451
- [own addObject:[token copy]];
452
- }
453
- options.topBar.searchBar.tokens = own;
454
- }];
544
+ // Copies: the objects above belong to the applied tree, and two trees never share a group.
545
+ NSMutableArray<ANNSearchTokenOptions *> *kept =
546
+ [NSMutableArray arrayWithCapacity:tokens.count];
547
+ for (ANNSearchTokenOptions *token in tokens) {
548
+ [kept addObject:[token copy]];
549
+ }
550
+ [_viewController setUserChoice:ANNTokensChoice(kept) forKey:ANNTokensChoiceKey];
455
551
  }
456
552
  return ids;
457
553
  }
458
554
 
459
555
  #pragma mark - Suggestions
460
556
 
557
+ // A suggestion's key names its icon by its source, as a token's does (`ANNTokenKey`).
461
558
  static NSString *ANNSuggestionKey(ANNSearchSuggestionOptions *suggestion) {
462
559
  return [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n%@", [suggestion.identifier withDefault:@""],
463
560
  [suggestion.title withDefault:@""], [suggestion.accessibilityLabel withDefault:@""],
464
- [suggestion.sfSymbol withDefault:@""], [suggestion.icon withDefault:nil] ?: @""];
561
+ [suggestion.sfSymbol withDefault:@""], suggestion.icon.source.sourceKey ?: @""];
562
+ }
563
+
564
+ static NSArray<NSString *> *ANNSuggestionKeys(NSArray<ANNSearchSuggestionOptions *> *suggestions) {
565
+ NSMutableArray<NSString *> *keys = [NSMutableArray new];
566
+ for (ANNSearchSuggestionOptions *suggestion in suggestions) {
567
+ [keys addObject:ANNSuggestionKey(suggestion)];
568
+ }
569
+ return keys;
570
+ }
571
+
572
+ // The suggestions UIKit removed, by their keys: they stay out of the app's `suggestions` until the next merge that
573
+ // gives `suggestions`, any list, the same one again too (a fixed list sent on every search-updated event shows each
574
+ // time), as on Android, which shows whatever list a merge gives. After a merge without them the removed ones the
575
+ // screen's resolution no longer has leave the choice.
576
+ static ANNUserChoice *ANNRemovedSuggestionsChoice(NSArray<NSString *> *removed) {
577
+ return [ANNUserChoice choiceWithValue:removed
578
+ apply:^(ANNNavigationOptions *options, id value) {
579
+ NSArray<ANNSearchSuggestionOptions *> *suggestions = options.topBar.searchBar.suggestions;
580
+ if (!suggestions) {
581
+ return;
582
+ }
583
+ NSSet<NSString *> *gone = [NSSet setWithArray:value];
584
+ NSMutableArray<ANNSearchSuggestionOptions *> *kept = [NSMutableArray new];
585
+ for (ANNSearchSuggestionOptions *suggestion in suggestions) {
586
+ if (![gone containsObject:ANNSuggestionKey(suggestion)]) {
587
+ [kept addObject:suggestion];
588
+ }
589
+ }
590
+ options.topBar.searchBar.suggestions = kept;
591
+ }
592
+ merged:^id(id value, ANNNavigationOptions *merge, ANNNavigationOptions *own,
593
+ ANNNavigationOptions *before, ANNNavigationOptions *after) {
594
+ if (merge.topBar.searchBar.suggestions) {
595
+ return nil;
596
+ }
597
+ NSSet<NSString *> *now =
598
+ [NSSet setWithArray:ANNSuggestionKeys(after.topBar.searchBar.suggestions)];
599
+ NSArray<NSString *> *removed = value;
600
+ NSMutableArray<NSString *> *kept = [NSMutableArray new];
601
+ for (NSString *key in removed) {
602
+ if ([now containsObject:key]) {
603
+ [kept addObject:key];
604
+ }
605
+ }
606
+ if (kept.count == 0) {
607
+ return nil;
608
+ }
609
+ return kept.count == removed.count ? value : kept;
610
+ }];
465
611
  }
466
612
 
467
613
  - (void)applySuggestions:(NSArray<ANNSearchSuggestionOptions *> *)suggestions
@@ -491,13 +637,20 @@ static NSString *ANNSuggestionKey(ANNSearchSuggestionOptions *suggestion) {
491
637
  search.searchSuggestions = items.count ? items : nil;
492
638
  }
493
639
 
494
- // UIKit has removed the suggestions: so do the screen's options, and the next ones from mergeOptions show.
640
+ // UIKit has removed the suggestions: the screen's user layer keeps them out, with those it removed before, until the
641
+ // next mergeOptions that gives suggestions, which show.
495
642
  - (void)syncSuggestions:(UISearchController *)search API_AVAILABLE(ios(16.0)) {
496
643
  if (_appliedSuggestions.count && !search.searchSuggestions.count) {
644
+ NSArray<NSString *> *removed = _appliedSuggestions;
497
645
  _appliedSuggestions = @[];
498
- [_viewController changeOwnOptions:^(ANNNavigationOptions *options) {
499
- options.topBar.searchBar.suggestions = @[];
500
- }];
646
+ UIViewController<ANNLayoutProtocol> *viewController = _viewController;
647
+ ANNUserChoice *before = [viewController userChoiceForKey:ANNSuggestionsChoiceKey];
648
+ NSArray<NSString *> *removedBefore = before.value;
649
+ if (removedBefore.count) {
650
+ removed = [removedBefore arrayByAddingObjectsFromArray:removed];
651
+ }
652
+ [viewController setUserChoice:ANNRemovedSuggestionsChoice(removed)
653
+ forKey:ANNSuggestionsChoiceKey];
501
654
  }
502
655
  }
503
656
 
@@ -606,14 +759,12 @@ static NSString *ANNSuggestionKey(ANNSearchSuggestionOptions *suggestion) {
606
759
  }
607
760
  }
608
761
 
609
- // The pick is written to the screen's options, so it stays when the options apply again. UIKit then updates the
762
+ // The pick is kept in the screen's user layer, so it stays when the options apply again. UIKit then updates the
610
763
  // search results: searchBarUpdated carries the new scope.
611
764
  - (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope {
612
765
  NSString *scope = [self selectedScope:searchBar];
613
766
  if (scope) {
614
- [_viewController changeOwnOptions:^(ANNNavigationOptions *options) {
615
- options.topBar.searchBar.selectedScope = [Text withValue:scope];
616
- }];
767
+ [_viewController setUserChoice:ANNScopeChoice(scope) forKey:ANNScopeChoiceKey];
617
768
  }
618
769
  }
619
770
 
@@ -88,6 +88,11 @@ NS_ASSUME_NONNULL_BEGIN
88
88
  /// had no large title: a large title that doesn't fold into the list fills its place there until the screen appeared.
89
89
  - (void)shownFromLowerBarWithBottom:(CGFloat)oldBarBottom;
90
90
 
91
+ /// iOS 26, a push starts that brings the screen in with its content drawn, before UIKit's push: its large title keeps
92
+ /// a place in the screen for the whole transition (`screenInPush:` of `ANNLargeTitlePresenter`). Nothing for a screen
93
+ /// that didn't wait parked for its content: its content comes in the push.
94
+ - (void)arrivesByPushOn:(UINavigationController *)navigation;
95
+
91
96
  /// iOS 26, a pop or a swipe back starts that reveals the screen, after its will-appear and before UIKit lays it out
92
97
  /// for the transition: its large title keeps a place in the window for the whole transition. Returns whether the title
93
98
  /// is in that place.
@@ -2,6 +2,7 @@
2
2
  #import "ANNBackButtonPresenter.h"
3
3
  #import "ANNBarAppearance.h"
4
4
  #import "ANNComponentPresenter.h"
5
+ #import "ANNComponentViewController.h"
5
6
  #import "ANNExternalViewController.h"
6
7
  #import "ANNLargeTitlePresenter.h"
7
8
  #import "ANNMainList.h"
@@ -13,6 +14,7 @@
13
14
  #import "ANNTitleGlassView.h"
14
15
  #import "ANNTitleView.h"
15
16
  #import "ANNToolbarPresenter.h"
17
+ #import "ANNUserChoice.h"
16
18
  #import "UIViewController+LayoutProtocol.h"
17
19
 
18
20
  // The key of the bar's handler on the screen's content mounts.
@@ -59,6 +61,30 @@ static NSArray<UIMenuElement *> *ANNRenameElementsOf(NSArray<UIMenuElement *> *e
59
61
  return kept;
60
62
  }
61
63
 
64
+ static NSString *const ANNRenameChoiceKey = @"topBar.title.text";
65
+
66
+ // The title the user gave: it stays over the app's `title.text` until a merge gives another text than the app had
67
+ // there (the own options', else the title the screen resolved to before the merge, as the other choices); a re-render
68
+ // that sends the old title again keeps it.
69
+ static ANNUserChoice *ANNRenameChoice(NSString *title) {
70
+ return [ANNUserChoice choiceWithValue:title
71
+ apply:^(ANNNavigationOptions *options, id value) {
72
+ options.topBar.title.text = [Text withValue:value];
73
+ }
74
+ merged:^id(id value, ANNNavigationOptions *merge, ANNNavigationOptions *own,
75
+ ANNNavigationOptions *before, ANNNavigationOptions *after) {
76
+ Text *text = merge.topBar.title.text;
77
+ if (!text.hasValue) {
78
+ return value;
79
+ }
80
+ Text *ownText = own.topBar.title.text;
81
+ Text *was = ownText.hasValue ? ownText : before.topBar.title.text;
82
+ NSString *merged = [text withDefault:nil];
83
+ NSString *had = [was withDefault:nil];
84
+ return merged == had || [merged isEqualToString:had] ? value : nil;
85
+ }];
86
+ }
87
+
62
88
  @interface ANNTopBarPresenter () <UINavigationItemRenameDelegate>
63
89
  @end
64
90
 
@@ -495,8 +521,11 @@ static NSArray<UIMenuElement *> *ANNRenameElementsOf(NSArray<UIMenuElement *> *e
495
521
  // The layers of the title: the screen's own text hides a component of its stack or of the defaults; a component in the
496
522
  // screen's own options shows over its own text.
497
523
  - (BOOL)ownTextHidesComponent {
498
- ANNTopBarTitleOptions *own = _screen.options.topBar.title;
499
- return own.text.hasValue && !own.text.isNull && !ANNHasComponentName(own.component);
524
+ UIViewController *screen = _screen;
525
+ ANNTopBarTitleOptions *own = screen.options.topBar.title;
526
+ BOOL ownText = (own.text.hasValue && !own.text.isNull) ||
527
+ [screen userChoiceForKey:ANNRenameChoiceKey] != nil;
528
+ return ownText && !ANNHasComponentName(own.component);
500
529
  }
501
530
 
502
531
  // iOS 26: the system subtitle under the title and under the large title, with only the attributes given (a color, or
@@ -644,11 +673,12 @@ static NSArray<UIMenuElement *> *ANNRenameElementsOf(NSArray<UIMenuElement *> *e
644
673
  __weak ANNBarButtons *buttons = _buttons;
645
674
  UIMenu *menu = [title.menu menuWithPickHandler:^(NSString *itemId) {
646
675
  [buttons menuItemPicked:itemId
647
- menuOf:^ANNMenuOptions *(ANNNavigationOptions *resolved) {
648
- return resolved.topBar.title.menu;
676
+ choiceKey:@"topBar.title.menu"
677
+ menuOf:^ANNMenuOptions *(ANNNavigationOptions *options) {
678
+ return options.topBar.title.menu;
649
679
  }
650
- setMenu:^(ANNNavigationOptions *own, ANNMenuOptions *copy) {
651
- own.topBar.title.menu = copy;
680
+ setMenu:^(ANNNavigationOptions *options, ANNMenuOptions *copy) {
681
+ options.topBar.title.menu = copy;
652
682
  }];
653
683
  }];
654
684
  menuTitle = menu.title;
@@ -659,8 +689,8 @@ static NSArray<UIMenuElement *> *ANNRenameElementsOf(NSArray<UIMenuElement *> *e
659
689
 
660
690
  #pragma mark - UINavigationItemRenameDelegate
661
691
 
662
- // Empty or blank: the old title back, no event; the same: nothing; new: the screen's own `title.text`, the title drawn
663
- // again, then `ANN.TitleRenamed`.
692
+ // Empty or blank: the old title back, no event; the same: nothing; new: `title.text` in the screen's user layer, the
693
+ // title drawn again, then `ANN.TitleRenamed`.
664
694
  - (void)navigationItem:(UINavigationItem *)navigationItem
665
695
  didEndRenamingWithTitle:(NSString *)title {
666
696
  UIViewController *screen = _screen;
@@ -677,9 +707,7 @@ static NSArray<UIMenuElement *> *ANNRenameElementsOf(NSArray<UIMenuElement *> *e
677
707
  if ([title isEqualToString:old]) {
678
708
  return;
679
709
  }
680
- [screen changeOwnOptions:^(ANNNavigationOptions *own) {
681
- own.topBar.title.text = [Text withValue:title];
682
- }];
710
+ [screen setUserChoice:ANNRenameChoice(title) forKey:ANNRenameChoiceKey];
683
711
  ANNNavigationOptions *options = [screen resolveOptionsWithDefault];
684
712
  [self applyTitle:options textWins:NO];
685
713
  NSString *componentId = screen.layoutInfo.componentId;
@@ -1101,6 +1129,27 @@ static NSArray<UIMenuElement *> *ANNRenameElementsOf(NSArray<UIMenuElement *> *e
1101
1129
  options:[screen resolveOptionsWithDefault]];
1102
1130
  }
1103
1131
 
1132
+ // The top of the safe area the screen takes on the stack is that of its place while it waited parked: UIKit lays it out
1133
+ // only inside the push.
1134
+ - (void)arrivesByPushOn:(UINavigationController *)navigation {
1135
+ UIViewController *screen = _screen;
1136
+ if (_native || ![screen isKindOfClass:ANNComponentViewController.class]) {
1137
+ return;
1138
+ }
1139
+ ANNComponentViewController *component = (ANNComponentViewController *)screen;
1140
+ ANNScreenPlace * (^placeWhileParked)(void) = component.placeWhileParked;
1141
+ ANNScreenPlace *place = placeWhileParked ? placeWhileParked() : nil;
1142
+ if (!place || !component.reactView.isReady) {
1143
+ return;
1144
+ }
1145
+ ANNNavigationOptions *options = [screen resolveOptionsWithDefault];
1146
+ [_largeTitle screenInPush:options
1147
+ list:[self followedList]
1148
+ inlineInRow:[self inlineInRow:options]
1149
+ top:place.safeArea.top
1150
+ stackTop:navigation.view.safeAreaInsets.top];
1151
+ }
1152
+
1104
1153
  // A native screen's views are the app's: the core lays nothing into them. The band behind the title is filled where
1105
1154
  // the title is, and the look follows it, before UIKit lays the screen out for the transition.
1106
1155
  - (BOOL)revealedByPop {
@@ -10,6 +10,7 @@
10
10
  #import "ANNEventContract.h"
11
11
  #import "ANNExternalViewController.h"
12
12
  #import "ANNHinge.h"
13
+ #import "ANNJavaScriptCalls.h"
13
14
  #import "ANNLayoutReader.h"
14
15
  #import "ANNModalCoordinator.h"
15
16
  #import "ANNNativeScreenCalls.h"
@@ -29,6 +30,9 @@
29
30
  #import "ANNStackCommands.h"
30
31
  #import "ApexNavigation+Internal.h"
31
32
  #import "UIViewController+LayoutProtocol.h"
33
+ #import <React/RCTCallInvokerModule.h>
34
+ #import <React/RCTInitializing.h>
35
+ #import <React/RCTInvalidating.h>
32
36
 
33
37
  // How many modules this process made: the first one is a cold start, the next ones come with reloads of JavaScript.
34
38
  static NSUInteger ANNTurboModuleCount;
@@ -84,6 +88,11 @@ static BOOL ANNFindsScreen(NSString *componentId, ANNCommandAnswer *answer) {
84
88
  return NO;
85
89
  }
86
90
 
91
+ // The module's call invoker and queue go to `ANNJavaScriptCalls`, so that a step of the core can come after the calls
92
+ // JavaScript made (a push after the options of its screen's first commit).
93
+ @interface ANNTurboModule () <RCTCallInvokerModule, RCTInitializing, RCTInvalidating>
94
+ @end
95
+
87
96
  // Every asynchronous method takes the one path of `ANNCommandRunner`: on the module's queue the layout is read and
88
97
  // checked whole, then one task on the main queue runs the command with its area's class. A question answers at once on
89
98
  // the JavaScript thread what it answers before the first data.
@@ -94,6 +103,11 @@ static BOOL ANNFindsScreen(NSString *componentId, ANNCommandAnswer *answer) {
94
103
 
95
104
  RCT_EXPORT_MODULE(ANNTurboModule)
96
105
 
106
+ // React Native gives the module its shared queue through this (it has none of its own) and its call invoker, both
107
+ // before `initialize`.
108
+ @synthesize methodQueue = _methodQueue;
109
+ @synthesize callInvoker = _callInvoker;
110
+
97
111
  + (BOOL)requiresMainQueueSetup {
98
112
  return NO;
99
113
  }
@@ -109,6 +123,14 @@ RCT_EXPORT_MODULE(ANNTurboModule)
109
123
  return self;
110
124
  }
111
125
 
126
+ - (void)initialize {
127
+ [ANNJavaScriptCalls setCallInvoker:_callInvoker methodQueue:_methodQueue ofModule:self];
128
+ }
129
+
130
+ - (void)invalidate {
131
+ [ANNJavaScriptCalls moduleWent:self];
132
+ }
133
+
112
134
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
113
135
  (const facebook::react::ObjCTurboModule::InitParams &)params {
114
136
  return std::make_shared<facebook::react::NativeANNTurboModuleSpecJSI>(params);
@@ -139,7 +161,7 @@ RCT_EXPORT_MODULE(ANNTurboModule)
139
161
  // navigation has a host. React Native starts its surface once the bundle ran.
140
162
  - (void)startHost {
141
163
  [ANNCommandRunner runVoid:@"startHost"
142
- what:@"startHost"
164
+ what:@"start the host of the slots"
143
165
  body:^{
144
166
  RCTHost *host = [ApexNavigation getHost];
145
167
  if (host) {
@@ -170,22 +192,23 @@ RCT_EXPORT_MODULE(ANNTurboModule)
170
192
  }];
171
193
  }
172
194
 
173
- // `mergeOptions`: a node nobody has (not made yet, gone, a reload) takes nothing, without a log; a node of a modal that
174
- // goes with the old root is not shown (logged). The values that could be read merge even when one couldn't. The whole
175
- // apply is one Core Animation transaction, closed on any error. Then the modal or the overlay of the node takes the
176
- // merge once (`mergeOptions` step 5).
195
+ // `mergeOptions`: without the navigation's window it logs `nowhereToShow` (JavaScript keeps the options for the next
196
+ // root); a node nobody has (not made yet, gone, a reload) or a node of a modal that goes with the old root takes
197
+ // nothing, without a log, as on Android. The values that could be read merge even when one couldn't. The whole apply
198
+ // is one Core Animation transaction, closed on any error. Then the modal or the overlay of the node takes the merge
199
+ // once (`mergeOptions` step 5).
177
200
  - (void)mergeOptions:(NSString *)componentId options:(NSDictionary *)options {
178
201
  NSString *what = ANNWhat(@"merge the options of the screen '%@'", componentId);
179
202
  [ANNCommandRunner runVoid:@"mergeOptions"
180
203
  what:what
181
204
  body:^{
182
- UIViewController *node = [ANNNodeRegistry nodeForComponentId:componentId];
183
- if (!node) {
205
+ if (!ApexNavigation.mainWindow) {
206
+ [ANNCommandRunner logFailure:@"mergeOptions"
207
+ text:ANNErrorTexts.nowhereToShow];
184
208
  return;
185
209
  }
186
- if ([ANNModalCoordinator isWithOldRoot:node]) {
187
- NSString *text = [ANNErrorTexts screenNotShown:componentId];
188
- [ANNCommandRunner logFailure:@"mergeOptions" text:text];
210
+ UIViewController *node = ANNCommandNode(componentId);
211
+ if (!node) {
189
212
  return;
190
213
  }
191
214
  NSString *unreadablePath = nil;
@@ -244,13 +267,13 @@ RCT_EXPORT_MODULE(ANNTurboModule)
244
267
  }];
245
268
  }
246
269
 
247
- // iOS lays out no screen before its push: always null.
270
+ // iOS lays out no screen before its push: always null (`NSNull`; nil would reach JavaScript as `undefined`).
248
271
  - (void)preparePush:(NSString *)commandId
249
272
  componentId:(NSString *)componentId
250
273
  layout:(NSDictionary *)layout
251
274
  resolve:(RCTPromiseResolveBlock)resolve
252
275
  reject:(RCTPromiseRejectBlock)reject {
253
- [ANNErrorHandler resolve:resolve of:@"preparePush" value:nil];
276
+ [ANNErrorHandler resolve:resolve of:@"preparePush" value:NSNull.null];
254
277
  }
255
278
 
256
279
  - (void)cancelPreparedPush:(NSString *)componentId {
@@ -551,26 +574,21 @@ RCT_EXPORT_MODULE(ANNTurboModule)
551
574
  check:nil
552
575
  body:^(id layout, ANNCommandAnswer *answer) {
553
576
  // The links module answers once the launch links are known, at once in the background
554
- // (`getLaunchLink`): the URL, or null.
577
+ // (`getLaunchLink`): the URL, or null (`NSNull`; nil would reach JavaScript as `undefined`).
555
578
  [ANNDeepLinkQueue takeLaunchLink:^(NSURL *url) {
556
- [answer resolve:url.absoluteString];
579
+ [answer resolve:url.absoluteString ?: (id)NSNull.null];
557
580
  }];
558
581
  }];
559
582
  }
560
583
 
561
584
  - (void)updateNativeScreenProps:(NSString *)componentId update:(NSDictionary *)update {
562
585
  NSString *what = ANNWhat(@"updateNativeScreenProps for the screen '%@'", componentId);
563
- // At once, also during a transition; a node that isn't a native screen, or no node, takes nothing.
586
+ // At once, also during a transition; a node that isn't a native screen, no node, or a node of a modal that goes
587
+ // with the old root takes nothing, without a log, as on Android.
564
588
  [ANNCommandRunner runVoid:@"updateNativeScreenProps"
565
589
  what:what
566
590
  body:^{
567
591
  UIViewController *node = ANNCommandNode(componentId);
568
- // A node of a modal that goes with the old root is not shown.
569
- if (!node && [ANNNodeRegistry nodeForComponentId:componentId]) {
570
- NSString *text = [ANNErrorTexts screenNotShown:componentId];
571
- [ANNCommandRunner logFailure:@"updateNativeScreenProps" text:text];
572
- return;
573
- }
574
592
  if ([node isKindOfClass:ANNExternalViewController.class]) {
575
593
  [((ANNExternalViewController *)node).nativeScreenContext
576
594
  applyUpdate:update];