@apexnative/navigation 1.0.0-alpha.1 → 1.0.0-alpha.3

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 (163) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +147 -51
  3. package/android/CLAUDE.md +31 -6
  4. package/android/CORE.md +7 -2
  5. package/android/RECIPES.md +4 -3
  6. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +29 -1
  7. package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
  8. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +5 -4
  9. package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
  10. package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +3 -2
  11. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +1 -1
  12. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +5 -4
  13. package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +7 -6
  14. package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +3 -35
  15. package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
  16. package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +101 -87
  17. package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
  18. package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
  19. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
  20. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +76 -13
  21. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
  22. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
  23. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
  24. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
  25. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +86 -16
  26. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
  27. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +41 -23
  28. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +24 -20
  29. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +25 -0
  30. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +4 -0
  31. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +42 -4
  32. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +29 -6
  33. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +2 -0
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -0
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +5 -3
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +4 -7
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +8 -1
  40. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +16 -2
  41. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +287 -0
  42. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +148 -6
  43. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +24 -0
  44. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +51 -11
  45. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
  46. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +105 -2
  47. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +123 -8
  48. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +19 -0
  49. package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
  50. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +5 -5
  51. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
  52. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +85 -27
  53. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +5 -4
  54. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
  55. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
  56. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +45 -1
  57. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +3 -5
  58. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +3 -5
  59. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +3 -5
  60. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +2 -0
  61. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +4 -6
  62. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +4 -6
  63. package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +15 -0
  64. package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +137 -1
  65. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +8 -1
  66. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +26 -4
  67. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +4 -3
  68. package/cli/app.js +1 -1
  69. package/cli/project.js +6 -2
  70. package/cli/setup/index.js +13 -0
  71. package/cli/setup/steps/appDelegate.js +66 -11
  72. package/cli/setup/steps/sceneDelegate.js +108 -0
  73. package/docs-md/architecture/transitions.md +7 -1
  74. package/docs-md/containers/modals.md +4 -2
  75. package/docs-md/containers/overlays.md +3 -1
  76. package/docs-md/containers/side-menu.md +4 -2
  77. package/docs-md/containers/tabs.md +2 -0
  78. package/docs-md/flows/forms-in-a-modal.md +1 -1
  79. package/docs-md/guides/deep-links.md +15 -33
  80. package/docs-md/help/glossary.md +1 -1
  81. package/docs-md/index.json +8 -0
  82. package/docs-md/screen-ui/back-and-gestures.md +6 -0
  83. package/docs-md/screen-ui/system-ui.md +1 -1
  84. package/docs-md/start/installation.md +1 -0
  85. package/ios/Animations/ANNTransitionAnimator.mm +13 -11
  86. package/ios/Animations/ANNTransitionWait.mm +6 -7
  87. package/ios/Animations/Elements/ANNElementFinder.h +57 -0
  88. package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
  89. package/ios/Animations/Elements/ANNElementTransition.h +7 -5
  90. package/ios/Animations/Elements/ANNElementTransition.mm +17 -19
  91. package/ios/Animations/Elements/ANNSharedElementFlight.h +13 -8
  92. package/ios/Animations/Elements/ANNSharedElementFlight.mm +59 -24
  93. package/ios/CLAUDE.md +10 -2
  94. package/ios/CORE.md +3 -2
  95. package/ios/Contract/ANNCommandAnswer.h +4 -3
  96. package/ios/Contract/ANNCommandAnswer.mm +12 -2
  97. package/ios/Contract/ANNCommandRunner.h +2 -2
  98. package/ios/Contract/ANNCommandRunner.mm +1 -1
  99. package/ios/Contract/ANNErrorTexts.h +6 -2
  100. package/ios/Contract/ANNErrorTexts.mm +18 -10
  101. package/ios/Core/ANNDeepLinkQueue.h +20 -1
  102. package/ios/Core/ANNDeepLinkQueue.mm +107 -12
  103. package/ios/Core/ANNErrorHandler.h +8 -3
  104. package/ios/Core/ANNErrorHandler.mm +17 -1
  105. package/ios/Core/ANNEventContract.h +246 -0
  106. package/ios/Core/ANNEventContract.mm +225 -0
  107. package/ios/Core/ANNEventEmitter.h +5 -1
  108. package/ios/Core/ANNEventEmitter.mm +117 -80
  109. package/ios/Core/ANNHinge.mm +6 -4
  110. package/ios/Core/ANNSceneDelegate.mm +25 -15
  111. package/ios/Core/ANNSystemUI.h +3 -2
  112. package/ios/Core/ANNSystemUI.mm +7 -0
  113. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +6 -4
  114. package/ios/Layouts/Modal/ANNModalCoordinator.mm +1 -1
  115. package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
  116. package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
  117. package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
  118. package/ios/Layouts/Root/ANNRootCoordinator.mm +12 -3
  119. package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
  120. package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
  121. package/ios/Layouts/Screen/ANNComponentViewController.mm +28 -7
  122. package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
  123. package/ios/Layouts/Screen/ANNScreenInsets.mm +6 -4
  124. package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -6
  125. package/ios/Layouts/SplitView/ANNSplitViewController.mm +4 -7
  126. package/ios/Options/ANNSheetOptions.h +4 -1
  127. package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
  128. package/ios/RECIPES.md +2 -1
  129. package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
  130. package/ios/TurboModules/ANNTurboModule.mm +5 -2
  131. package/lib/module/ARCHITECTURE.md +7 -4
  132. package/lib/module/api/app/createNavigation.js +2 -0
  133. package/lib/module/api/app/createNavigation.js.map +1 -1
  134. package/lib/module/api/hooks/useNavigation.js +8 -3
  135. package/lib/module/api/hooks/useNavigation.js.map +1 -1
  136. package/lib/module/api/linking/dispatchedLinks.js +23 -0
  137. package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
  138. package/lib/module/interfaces/contract/errors.json +275 -0
  139. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  140. package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
  141. package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
  142. package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
  143. package/lib/typescript/interfaces/Events.d.ts +9 -0
  144. package/lib/typescript/interfaces/Events.d.ts.map +1 -1
  145. package/lib/typescript/interfaces/options/animations.d.ts +27 -18
  146. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  147. package/lib/typescript/interfaces/options/modals.d.ts +33 -24
  148. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  149. package/lib/typescript/interfaces/options/sideMenu.d.ts +7 -6
  150. package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
  151. package/lib/typescript/interfaces/options/system.d.ts +10 -6
  152. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  153. package/package.json +1 -1
  154. package/src/ARCHITECTURE.md +7 -4
  155. package/src/api/app/createNavigation.ts +2 -0
  156. package/src/api/hooks/useNavigation.ts +8 -3
  157. package/src/api/linking/dispatchedLinks.ts +18 -0
  158. package/src/interfaces/Events.ts +10 -0
  159. package/src/interfaces/contract/errors.json +275 -0
  160. package/src/interfaces/options/animations.ts +27 -18
  161. package/src/interfaces/options/modals.ts +33 -24
  162. package/src/interfaces/options/sideMenu.ts +7 -6
  163. package/src/interfaces/options/system.ts +10 -6
@@ -1,3 +1,5 @@
1
+ // Made by scripts/contract.mjs from src/interfaces/contract/errors.json: change that file and run the script again.
2
+
1
3
  #import "ANNErrorTexts.h"
2
4
 
3
5
  NSExceptionName const ANNCommandRejectionException = @"ANNCommandRejectionException";
@@ -59,8 +61,8 @@ static NSString *ANNText(NSString *format, ...) {
59
61
  }
60
62
 
61
63
  + (NSString *)columnNotStack:(NSString *)componentId column:(NSString *)column {
62
- return ANNText(@"the '%@' column of the split view of the screen '%@' is not a stack", column,
63
- componentId);
64
+ return ANNText(@"the '%@' column of the split view of the screen '%@' is not a stack",
65
+ column, componentId);
64
66
  }
65
67
 
66
68
  + (NSString *)noScreensForStack:(NSString *)componentId {
@@ -74,7 +76,8 @@ static NSString *ANNText(NSString *format, ...) {
74
76
  #pragma mark - Root, modals, the app
75
77
 
76
78
  + (NSString *)setRootReplaced:(NSString *)rootId {
77
- return ANNText(@"setRoot was replaced by a newer setRoot before the root '%@' finished showing",
79
+ return ANNText(@"setRoot was replaced by a newer setRoot before the root '%@' finished "
80
+ @"showing",
78
81
  rootId);
79
82
  }
80
83
 
@@ -141,8 +144,8 @@ static NSString *ANNText(NSString *format, ...) {
141
144
  }
142
145
 
143
146
  + (NSString *)tooManyTabs:(NSString *)tabsId count:(NSUInteger)count {
144
- return ANNText(@"the tabs '%@' have %lu tabs: Android shows at most 5", tabsId,
145
- (unsigned long)count);
147
+ return ANNText(@"the tabs '%@' have %lu tabs: Android shows at most 5",
148
+ tabsId, (unsigned long)count);
146
149
  }
147
150
 
148
151
  + (NSString *)splitViewColumns:(NSString *)splitViewId {
@@ -181,8 +184,8 @@ static NSString *ANNText(NSString *format, ...) {
181
184
  }
182
185
 
183
186
  + (NSString *)sideMenuInSide:(NSString *)menuId {
184
- return ANNText(@"the side menu '%@' has a side menu in a side: a side menu doesn't go inside a "
185
- @"side menu",
187
+ return ANNText(@"the side menu '%@' has a side menu in a side: a side menu doesn't go inside "
188
+ @"a side menu",
186
189
  menuId);
187
190
  }
188
191
 
@@ -191,8 +194,8 @@ static NSString *ANNText(NSString *format, ...) {
191
194
  }
192
195
 
193
196
  + (NSString *)splitViewInColumn:(NSString *)splitViewId {
194
- return ANNText(@"the split view '%@' has a column of type 'SplitView': a split view doesn't go "
195
- @"inside a split view",
197
+ return ANNText(@"the split view '%@' has a column of type 'SplitView': a split view doesn't "
198
+ @"go inside a split view",
196
199
  splitViewId);
197
200
  }
198
201
 
@@ -208,7 +211,8 @@ static NSString *ANNText(NSString *format, ...) {
208
211
  }
209
212
 
210
213
  + (NSString *)unreadableDefaultOption:(NSString *)path {
211
- return ANNText(@"the option '%@' of the default options has a value the navigation can't read", path);
214
+ return ANNText(@"the option '%@' of the default options has a value the navigation can't read",
215
+ path);
212
216
  }
213
217
 
214
218
  + (NSString *)previewGone:(NSString *)previewId {
@@ -223,6 +227,10 @@ static NSString *ANNText(NSString *format, ...) {
223
227
  return ANNText(@"could not run '%@': the native core doesn't do it yet", method);
224
228
  }
225
229
 
230
+ + (NSString *)notAnswered:(NSString *)method {
231
+ return ANNText(@"could not run '%@': the native core let it go without an answer", method);
232
+ }
233
+
226
234
  + (NSException *)rejection:(NSString *)text {
227
235
  return [NSException exceptionWithName:ANNCommandRejectionException reason:text userInfo:nil];
228
236
  }
@@ -1,4 +1,4 @@
1
- #import <Foundation/Foundation.h>
1
+ #import <UIKit/UIKit.h>
2
2
 
3
3
  NS_ASSUME_NONNULL_BEGIN
4
4
 
@@ -20,6 +20,25 @@ typedef void (^ANNLaunchLinkCompletion)(NSURL *_Nullable url);
20
20
  /// Hands `url` to `Linking`, or keeps it until React content first appears and no `takeLaunchLink:` waits.
21
21
  + (void)dispatchURL:(NSURL *)url;
22
22
 
23
+ /**
24
+ * Runs `forward`, which hands the scene's link to the app delegate (`ANNSceneDelegate`). The URL the app delegate
25
+ * dispatches in it keeps the scene and its context, which React Native 0.88 and newer need to take a scene's link.
26
+ */
27
+ + (void)forwardURLContext:(UIOpenURLContext *)context
28
+ ofScene:(UIScene *)scene
29
+ through:(NS_NOESCAPE dispatch_block_t)forward;
30
+
31
+ /// `forwardURLContext:ofScene:through:` for a web link (`webpageURL`) of the scene.
32
+ + (void)forwardUserActivity:(NSUserActivity *)activity
33
+ ofScene:(UIScene *)scene
34
+ through:(NS_NOESCAPE dispatch_block_t)forward;
35
+
36
+ /// Hands the scene's link to `Linking` at once, past the queue: for an app delegate that doesn't take links.
37
+ + (void)openURLContext:(UIOpenURLContext *)context ofScene:(UIScene *)scene;
38
+
39
+ /// `openURLContext:ofScene:` for a web link (`webpageURL`) of the scene; an activity without one does nothing.
40
+ + (void)openUserActivity:(NSUserActivity *)activity ofScene:(UIScene *)scene;
41
+
23
42
  /// The app's window scene connected (`+[ApexNavigation attachToWindowScene:]`).
24
43
  + (void)windowSceneDidConnect;
25
44
 
@@ -1,12 +1,41 @@
1
1
  #import "ANNDeepLinkQueue.h"
2
2
  #import "ANNAssert.h"
3
+ #import "ANNEventEmitter.h"
4
+ #import "ApexNavigation+Internal.h"
3
5
  #import <React/RCTLinkingManager.h>
4
6
  #import <React/RCTRootView.h>
5
7
  #import <UIKit/UIKit.h>
6
8
 
9
+ // A link on its way to `Linking`, with the scene's own context or user activity it came in when it came from a scene.
10
+ @interface ANNDeepLink : NSObject
11
+ @property(nonatomic, readonly) NSURL *url;
12
+ @property(nonatomic, readonly, nullable) UIOpenURLContext *context;
13
+ @property(nonatomic, readonly, nullable) NSUserActivity *activity;
14
+ @property(nonatomic, readonly, weak, nullable) UIScene *scene;
15
+ @end
16
+
17
+ @implementation ANNDeepLink
18
+
19
+ - (instancetype)initWithURL:(NSURL *)url
20
+ context:(UIOpenURLContext *)context
21
+ activity:(NSUserActivity *)activity
22
+ scene:(UIScene *)scene {
23
+ if (self = [super init]) {
24
+ _url = url;
25
+ _context = context;
26
+ _activity = activity;
27
+ _scene = scene;
28
+ }
29
+ return self;
30
+ }
31
+
32
+ @end
33
+
7
34
  // Main queue only.
8
35
  static BOOL ANNDeepLinkQueueStarted = NO;
9
- static NSMutableArray<NSURL *> *ANNPendingDeepLinkURLs;
36
+ static NSMutableArray<ANNDeepLink *> *ANNPendingDeepLinks;
37
+ // The scene's link the scene delegate is handing to the app delegate: the app delegate dispatches its URL.
38
+ static ANNDeepLink *ANNForwardedSceneLink;
10
39
  // React content appeared: `RCTLinkingManager` exists and JS listens to `url` events.
11
40
  static BOOL ANNReactContentAppeared = NO;
12
41
  // Every URL that launched the app was dispatched.
@@ -20,7 +49,7 @@ static ANNLaunchLinkCompletion ANNWaitingLaunchLinkCompletion;
20
49
 
21
50
  + (void)initialize {
22
51
  if (self == ANNDeepLinkQueue.class) {
23
- ANNPendingDeepLinkURLs = [NSMutableArray new];
52
+ ANNPendingDeepLinks = [NSMutableArray new];
24
53
  }
25
54
  }
26
55
 
@@ -46,11 +75,44 @@ static ANNLaunchLinkCompletion ANNWaitingLaunchLinkCompletion;
46
75
 
47
76
  + (void)dispatchURL:(NSURL *)url {
48
77
  ANNAssertMainQueue();
78
+ // The URL of the scene's link keeps its context; one the app delegate changed is a URL of its own.
79
+ ANNDeepLink *link = [ANNForwardedSceneLink.url isEqual:url]
80
+ ? ANNForwardedSceneLink
81
+ : [[ANNDeepLink alloc] initWithURL:url context:nil activity:nil scene:nil];
49
82
  if (ANNReactContentAppeared && ANNWaitingLaunchLinkCompletion == nil) {
50
- [self openURL:url];
83
+ [self openLink:link];
84
+ return;
85
+ }
86
+ [ANNPendingDeepLinks addObject:link];
87
+ }
88
+
89
+ + (void)forwardURLContext:(UIOpenURLContext *)context
90
+ ofScene:(UIScene *)scene
91
+ through:(NS_NOESCAPE dispatch_block_t)forward {
92
+ [self forwardLink:[[ANNDeepLink alloc] initWithURL:context.URL context:context activity:nil scene:scene]
93
+ through:forward];
94
+ }
95
+
96
+ + (void)forwardUserActivity:(NSUserActivity *)activity
97
+ ofScene:(UIScene *)scene
98
+ through:(NS_NOESCAPE dispatch_block_t)forward {
99
+ if (activity.webpageURL == nil) {
100
+ forward();
101
+ return;
102
+ }
103
+ [self forwardLink:[[ANNDeepLink alloc] initWithURL:activity.webpageURL context:nil activity:activity scene:scene]
104
+ through:forward];
105
+ }
106
+
107
+ + (void)openURLContext:(UIOpenURLContext *)context ofScene:(UIScene *)scene {
108
+ [self openLink:[[ANNDeepLink alloc] initWithURL:context.URL context:context activity:nil scene:scene]];
109
+ }
110
+
111
+ + (void)openUserActivity:(NSUserActivity *)activity ofScene:(UIScene *)scene {
112
+ if (activity.webpageURL == nil) {
51
113
  return;
52
114
  }
53
- [ANNPendingDeepLinkURLs addObject:url];
115
+ [self openLink:[[ANNDeepLink alloc] initWithURL:activity.webpageURL context:nil activity:activity scene:scene]];
54
116
  }
55
117
 
56
118
  + (void)windowSceneDidConnect {
@@ -98,10 +160,10 @@ static ANNLaunchLinkCompletion ANNWaitingLaunchLinkCompletion;
98
160
  if (ANNWaitingLaunchLinkCompletion != nil) {
99
161
  return;
100
162
  }
101
- NSArray<NSURL *> *urls = [ANNPendingDeepLinkURLs copy];
102
- [ANNPendingDeepLinkURLs removeAllObjects];
103
- for (NSURL *url in urls) {
104
- [self openURL:url];
163
+ NSArray<ANNDeepLink *> *links = [ANNPendingDeepLinks copy];
164
+ [ANNPendingDeepLinks removeAllObjects];
165
+ for (ANNDeepLink *link in links) {
166
+ [self openLink:link];
105
167
  }
106
168
  }
107
169
 
@@ -124,13 +186,46 @@ static ANNLaunchLinkCompletion ANNWaitingLaunchLinkCompletion;
124
186
  // One link opens the app. The links queued before it are dropped: opened after it, they would take the app elsewhere.
125
187
  + (void)giveLaunchLink:(ANNLaunchLinkCompletion)completion {
126
188
  ANNLaunchLinkTaken = YES;
127
- NSURL *url = ANNPendingDeepLinkURLs.lastObject;
128
- [ANNPendingDeepLinkURLs removeAllObjects];
189
+ NSURL *url = ANNPendingDeepLinks.lastObject.url;
190
+ [ANNPendingDeepLinks removeAllObjects];
129
191
  completion(url);
130
192
  }
131
193
 
132
- + (void)openURL:(NSURL *)url {
133
- [RCTLinkingManager application:UIApplication.sharedApplication openURL:url options:@{}];
194
+ + (void)forwardLink:(ANNDeepLink *)link through:(NS_NOESCAPE dispatch_block_t)forward {
195
+ ANNAssertMainQueue();
196
+ ANNDeepLink *outer = ANNForwardedSceneLink;
197
+ ANNForwardedSceneLink = link;
198
+ forward();
199
+ ANNForwardedSceneLink = outer;
200
+ }
201
+
202
+ // React Native 0.88 and newer take a scene's links through its scene methods: in an app whose Info.plist names a scene
203
+ // delegate, its app delegate methods take none. The earlier versions have only the app delegate methods.
204
+ + (void)openLink:(ANNDeepLink *)link {
205
+ UIScene *scene = link.scene;
206
+ if (scene != nil && link.context != nil) {
207
+ SEL openContexts = NSSelectorFromString(@"scene:openURLContexts:");
208
+ if ([RCTLinkingManager respondsToSelector:openContexts]) {
209
+ IMP implementation = [RCTLinkingManager methodForSelector:openContexts];
210
+ ((void (*)(id, SEL, UIScene *, NSSet<UIOpenURLContext *> *))implementation)(
211
+ RCTLinkingManager.class, openContexts, scene, [NSSet setWithObject:link.context]);
212
+ return;
213
+ }
214
+ }
215
+ if (scene != nil && link.activity != nil) {
216
+ SEL continueActivity = NSSelectorFromString(@"scene:continueUserActivity:");
217
+ if ([RCTLinkingManager respondsToSelector:continueActivity]) {
218
+ IMP implementation = [RCTLinkingManager methodForSelector:continueActivity];
219
+ ((void (*)(id, SEL, UIScene *, NSUserActivity *))implementation)(
220
+ RCTLinkingManager.class, continueActivity, scene, link.activity);
221
+ return;
222
+ }
223
+ }
224
+ // A link of the app's own (`dispatchDeepLinkURL`) has no scene: on React Native 0.88 and newer, with a scene
225
+ // delegate in Info.plist, `Linking` doesn't take it from here. JavaScript hands it to `Linking` itself.
226
+ if (![RCTLinkingManager application:UIApplication.sharedApplication openURL:link.url options:@{}]) {
227
+ [ApexNavigation.eventEmitter sendDeepLinkDispatched:link.url];
228
+ }
134
229
  }
135
230
 
136
231
  @end
@@ -3,11 +3,16 @@
3
3
  NS_ASSUME_NONNULL_BEGIN
4
4
 
5
5
  /// How a failed command tells JavaScript: a rejection carries only a sentence, never a code of its own, and a command
6
- /// without a promise writes the sentence to the log. The sentences are those of `ANNErrorTexts`.
6
+ /// without a promise writes the sentence to the log. The sentences are those of `ANNErrorTexts`. The one place that
7
+ /// calls a command's promise blocks: a block that is nil (a caller that passed none) is never called.
7
8
  @interface ANNErrorHandler : NSObject
8
9
 
9
- /// Rejects a command's promise with the sentence alone: React Native puts its own default code on it.
10
- + (void)reject:(RCTPromiseRejectBlock)reject text:(NSString *)text;
10
+ /// Rejects a command's promise with the sentence alone: React Native puts its own default code on it. Without a
11
+ /// reject block the sentence goes to the log as `logFailureOf:text:` writes it.
12
+ + (void)reject:(nullable RCTPromiseRejectBlock)reject of:(NSString *)method text:(NSString *)text;
13
+
14
+ /// Resolves a command's promise with `value`. Without a resolve block one line goes to the log instead.
15
+ + (void)resolve:(nullable RCTPromiseResolveBlock)resolve of:(NSString *)method value:(nullable id)value;
11
16
 
12
17
  /// The log line of a command without a promise: `[ApexNavigation] <method>: <text>`.
13
18
  + (void)logFailureOf:(NSString *)method text:(NSString *)text;
@@ -1,12 +1,28 @@
1
1
  #import "ANNErrorHandler.h"
2
+ #import "ANNErrorTexts.h"
2
3
  #import <React/RCTLog.h>
3
4
 
4
5
  @implementation ANNErrorHandler
5
6
 
6
- + (void)reject:(RCTPromiseRejectBlock)reject text:(NSString *)text {
7
+ // Calling a nil block ends the app (`EXC_BAD_ACCESS` at 0x10, its invoke field): JavaScript always passes both blocks,
8
+ // so a nil one comes from a caller in native code, and the answer goes to the log.
9
+ + (void)reject:(RCTPromiseRejectBlock)reject of:(NSString *)method text:(NSString *)text {
10
+ if (!reject) {
11
+ [self logFailureOf:method text:text];
12
+ return;
13
+ }
7
14
  reject(nil, text, nil);
8
15
  }
9
16
 
17
+ + (void)resolve:(RCTPromiseResolveBlock)resolve of:(NSString *)method value:(id)value {
18
+ if (!resolve) {
19
+ [self logFailureOf:method
20
+ text:[ANNErrorTexts couldNot:@"resolve its promise" reason:@"the command came without one"]];
21
+ return;
22
+ }
23
+ resolve(value);
24
+ }
25
+
10
26
  + (void)logFailureOf:(NSString *)method text:(NSString *)text {
11
27
  RCTLogError(@"[ApexNavigation] %@: %@", method, text);
12
28
  }
@@ -0,0 +1,246 @@
1
+ // Made by scripts/contract.mjs from the event types of src/interfaces/: change them and run the script again.
2
+
3
+ #import <Foundation/Foundation.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ /// The names of the events `ANN.<Name>` (`event`) and the keys of their payloads, as the types of `src/interfaces/`
8
+ /// give them; what each field means is its JSDoc there. The object types a field holds have keys of their own. The
9
+ /// senders take every name and key from here: `ANNEventContract.CommandCompleted.commandId`.
10
+ FOUNDATION_EXPORT const struct ANNEventContractTable {
11
+ /// `ANN.AppLaunched`.
12
+ struct {
13
+ __unsafe_unretained NSString *event;
14
+ } AppLaunched;
15
+ /// `ANN.ArrangementChanged`.
16
+ struct {
17
+ __unsafe_unretained NSString *event;
18
+ __unsafe_unretained NSString *componentId;
19
+ __unsafe_unretained NSString *placement;
20
+ __unsafe_unretained NSString *layout;
21
+ __unsafe_unretained NSString *front;
22
+ __unsafe_unretained NSString *hidden;
23
+ } ArrangementChanged;
24
+ /// `ANN.BottomAccessoryPlacementChanged` (iOS).
25
+ struct {
26
+ __unsafe_unretained NSString *event;
27
+ __unsafe_unretained NSString *componentId;
28
+ __unsafe_unretained NSString *placement;
29
+ } BottomAccessoryPlacementChanged;
30
+ /// `ANN.BottomTabLongPressed`.
31
+ struct {
32
+ __unsafe_unretained NSString *event;
33
+ __unsafe_unretained NSString *selectedTabIndex;
34
+ __unsafe_unretained NSString *bottomTabsId;
35
+ } BottomTabLongPressed;
36
+ /// `ANN.BottomTabPressed`.
37
+ struct {
38
+ __unsafe_unretained NSString *event;
39
+ __unsafe_unretained NSString *tabIndex;
40
+ __unsafe_unretained NSString *bottomTabsId;
41
+ /// Optional.
42
+ __unsafe_unretained NSString *screenId;
43
+ __unsafe_unretained NSString *wasSelected;
44
+ } BottomTabPressed;
45
+ /// `ANN.BottomTabSelected`.
46
+ struct {
47
+ __unsafe_unretained NSString *event;
48
+ __unsafe_unretained NSString *selectedTabIndex;
49
+ __unsafe_unretained NSString *unselectedTabIndex;
50
+ __unsafe_unretained NSString *bottomTabsId;
51
+ } BottomTabSelected;
52
+ /// `ANN.CommandCompleted`.
53
+ struct {
54
+ __unsafe_unretained NSString *event;
55
+ __unsafe_unretained NSString *commandName;
56
+ __unsafe_unretained NSString *commandId;
57
+ __unsafe_unretained NSString *completionTime;
58
+ } CommandCompleted;
59
+ /// `ANN.ComponentDidAppear`.
60
+ struct {
61
+ __unsafe_unretained NSString *event;
62
+ __unsafe_unretained NSString *componentId;
63
+ __unsafe_unretained NSString *componentName;
64
+ __unsafe_unretained NSString *componentType;
65
+ } ComponentDidAppear;
66
+ /// `ANN.ComponentDidDisappear`.
67
+ struct {
68
+ __unsafe_unretained NSString *event;
69
+ __unsafe_unretained NSString *componentId;
70
+ __unsafe_unretained NSString *componentName;
71
+ __unsafe_unretained NSString *componentType;
72
+ } ComponentDidDisappear;
73
+ /// `ANN.ComponentWillAppear`.
74
+ struct {
75
+ __unsafe_unretained NSString *event;
76
+ __unsafe_unretained NSString *componentId;
77
+ __unsafe_unretained NSString *componentName;
78
+ __unsafe_unretained NSString *componentType;
79
+ } ComponentWillAppear;
80
+ /// `ANN.DeepLinkDispatched` (iOS).
81
+ struct {
82
+ __unsafe_unretained NSString *event;
83
+ __unsafe_unretained NSString *url;
84
+ } DeepLinkDispatched;
85
+ /// `ANN.HingeChanged`.
86
+ struct {
87
+ __unsafe_unretained NSString *event;
88
+ /// Optional.
89
+ __unsafe_unretained NSString *state;
90
+ /// Optional.
91
+ __unsafe_unretained NSString *angle;
92
+ } HingeChanged;
93
+ /// `ANN.KeyCommandPressed` (iOS).
94
+ struct {
95
+ __unsafe_unretained NSString *event;
96
+ __unsafe_unretained NSString *componentId;
97
+ __unsafe_unretained NSString *keyCommandId;
98
+ } KeyCommandPressed;
99
+ /// `ANN.ModalAttemptedToDismiss`.
100
+ struct {
101
+ __unsafe_unretained NSString *event;
102
+ __unsafe_unretained NSString *componentId;
103
+ } ModalAttemptedToDismiss;
104
+ /// `ANN.ModalDismissed`.
105
+ struct {
106
+ __unsafe_unretained NSString *event;
107
+ __unsafe_unretained NSString *componentId;
108
+ __unsafe_unretained NSString *componentName;
109
+ __unsafe_unretained NSString *modalsDismissed;
110
+ } ModalDismissed;
111
+ /// `ANN.NativeScreenCall`.
112
+ struct {
113
+ __unsafe_unretained NSString *event;
114
+ __unsafe_unretained NSString *componentId;
115
+ __unsafe_unretained NSString *callId;
116
+ __unsafe_unretained NSString *kind;
117
+ __unsafe_unretained NSString *name;
118
+ __unsafe_unretained NSString *args;
119
+ } NativeScreenCall;
120
+ /// `ANN.NativeScreenReleased`.
121
+ struct {
122
+ __unsafe_unretained NSString *event;
123
+ __unsafe_unretained NSString *componentId;
124
+ } NativeScreenReleased;
125
+ /// `ANN.NavigationButtonPressed`.
126
+ struct {
127
+ __unsafe_unretained NSString *event;
128
+ __unsafe_unretained NSString *componentId;
129
+ __unsafe_unretained NSString *buttonId;
130
+ /// Optional, iOS only.
131
+ __unsafe_unretained NSString *selected;
132
+ } NavigationButtonPressed;
133
+ /// `ANN.NavigationStateChanged`.
134
+ struct {
135
+ __unsafe_unretained NSString *event;
136
+ __unsafe_unretained NSString *screen;
137
+ __unsafe_unretained NSString *tab;
138
+ } NavigationStateChanged;
139
+ /// The object of `ANN.NavigationStateChanged.screen`.
140
+ struct {
141
+ __unsafe_unretained NSString *id;
142
+ __unsafe_unretained NSString *name;
143
+ } NavigationStateChangedScreen;
144
+ /// `ANN.PreviewCompleted` (iOS).
145
+ struct {
146
+ __unsafe_unretained NSString *event;
147
+ __unsafe_unretained NSString *componentId;
148
+ __unsafe_unretained NSString *previewComponentId;
149
+ } PreviewCompleted;
150
+ /// `ANN.ScreenInsetsChanged`.
151
+ struct {
152
+ __unsafe_unretained NSString *event;
153
+ __unsafe_unretained NSString *componentId;
154
+ __unsafe_unretained NSString *top;
155
+ __unsafe_unretained NSString *right;
156
+ __unsafe_unretained NSString *bottom;
157
+ __unsafe_unretained NSString *left;
158
+ } ScreenInsetsChanged;
159
+ /// `ANN.ScreenPopped`.
160
+ struct {
161
+ __unsafe_unretained NSString *event;
162
+ __unsafe_unretained NSString *componentId;
163
+ } ScreenPopped;
164
+ /// The object of `ANN.ScreenRegionsChanged.division`.
165
+ struct {
166
+ __unsafe_unretained NSString *x;
167
+ __unsafe_unretained NSString *y;
168
+ __unsafe_unretained NSString *width;
169
+ __unsafe_unretained NSString *height;
170
+ } ScreenRegionRect;
171
+ /// `ANN.ScreenRegionsChanged`.
172
+ struct {
173
+ __unsafe_unretained NSString *event;
174
+ __unsafe_unretained NSString *componentId;
175
+ __unsafe_unretained NSString *division;
176
+ __unsafe_unretained NSString *occlusion;
177
+ } ScreenRegionsChanged;
178
+ /// `ANN.SearchBarCancelPressed`.
179
+ struct {
180
+ __unsafe_unretained NSString *event;
181
+ __unsafe_unretained NSString *componentId;
182
+ } SearchBarCancelPressed;
183
+ /// `ANN.SearchBarSubmitted`.
184
+ struct {
185
+ __unsafe_unretained NSString *event;
186
+ __unsafe_unretained NSString *componentId;
187
+ __unsafe_unretained NSString *text;
188
+ /// Optional.
189
+ __unsafe_unretained NSString *scope;
190
+ /// Optional, iOS only.
191
+ __unsafe_unretained NSString *tokens;
192
+ } SearchBarSubmitted;
193
+ /// `ANN.SearchBarUpdated`.
194
+ struct {
195
+ __unsafe_unretained NSString *event;
196
+ __unsafe_unretained NSString *componentId;
197
+ __unsafe_unretained NSString *text;
198
+ __unsafe_unretained NSString *isFocused;
199
+ /// Optional.
200
+ __unsafe_unretained NSString *scope;
201
+ /// Optional, iOS only.
202
+ __unsafe_unretained NSString *tokens;
203
+ } SearchBarUpdated;
204
+ /// `ANN.SearchSuggestionPicked`.
205
+ struct {
206
+ __unsafe_unretained NSString *event;
207
+ __unsafe_unretained NSString *componentId;
208
+ __unsafe_unretained NSString *suggestionId;
209
+ } SearchSuggestionPicked;
210
+ /// `ANN.SheetDetentChanged`.
211
+ struct {
212
+ __unsafe_unretained NSString *event;
213
+ __unsafe_unretained NSString *componentId;
214
+ __unsafe_unretained NSString *detent;
215
+ } SheetDetentChanged;
216
+ /// `ANN.SlotDestroyed`.
217
+ struct {
218
+ __unsafe_unretained NSString *event;
219
+ __unsafe_unretained NSString *componentId;
220
+ } SlotDestroyed;
221
+ /// `ANN.SplitViewColumnsStacked`.
222
+ struct {
223
+ __unsafe_unretained NSString *event;
224
+ __unsafe_unretained NSString *componentId;
225
+ __unsafe_unretained NSString *stacked;
226
+ } SplitViewColumnsStacked;
227
+ /// `ANN.SplitViewDisplayModeChanged` (iOS).
228
+ struct {
229
+ __unsafe_unretained NSString *event;
230
+ __unsafe_unretained NSString *componentId;
231
+ __unsafe_unretained NSString *displayMode;
232
+ /// Optional.
233
+ __unsafe_unretained NSString *inspectorVisible;
234
+ } SplitViewDisplayModeChanged;
235
+ /// `ANN.TitleRenamed` (iOS).
236
+ struct {
237
+ __unsafe_unretained NSString *event;
238
+ __unsafe_unretained NSString *componentId;
239
+ __unsafe_unretained NSString *text;
240
+ } TitleRenamed;
241
+ } ANNEventContract;
242
+
243
+ /// The name of every event, `ANN.<Name>`.
244
+ FOUNDATION_EXPORT NSArray<NSString *> *ANNEventContractNames(void);
245
+
246
+ NS_ASSUME_NONNULL_END