react-native-tvos 0.73.4-0rc0 → 0.73.6-0

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 (32) hide show
  1. package/Libraries/Components/Touchable/TouchableHighlight.js +4 -1
  2. package/Libraries/Components/Touchable/TouchableNativeFeedback.js +1 -4
  3. package/Libraries/Components/Touchable/TouchableOpacity.js +1 -4
  4. package/Libraries/Components/Touchable/TouchableWithoutFeedback.js +1 -4
  5. package/Libraries/Core/ReactNativeVersion.js +2 -2
  6. package/Libraries/LogBox/Data/parseLogBoxLog.js +1 -1
  7. package/Libraries/Text/TextInput/RCTBaseTextInputShadowView.mm +48 -0
  8. package/README.md +23 -14
  9. package/React/Base/RCTVersion.m +2 -2
  10. package/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +246 -0
  11. package/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +20 -13
  12. package/React/React-RCTFabric.podspec +3 -2
  13. package/React/Views/RCTTVView.m +7 -0
  14. package/ReactAndroid/gradle.properties +1 -1
  15. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.java +2 -2
  16. package/ReactCommon/cxxreact/ReactNativeVersion.h +2 -2
  17. package/package.json +7 -7
  18. package/scripts/cocoapods/new_architecture.rb +5 -1
  19. package/scripts/cocoapods/utils.rb +21 -0
  20. package/scripts/codegen/generate-artifacts-executor.js +18 -11
  21. package/scripts/react_native_pods.rb +1 -0
  22. package/scripts/xcode/with-environment.sh +1 -1
  23. package/sdks/.hermesversion +1 -1
  24. package/sdks/hermes-engine/hermes-utils.rb +4 -3
  25. package/sdks/hermesc/linux64-bin/hermesc +0 -0
  26. package/sdks/hermesc/osx-bin/hermes +0 -0
  27. package/sdks/hermesc/osx-bin/hermesc +0 -0
  28. package/sdks/hermesc/win64-bin/hermesc.exe +0 -0
  29. package/sdks/hermesc/win64-bin/msvcp140.dll +0 -0
  30. package/sdks/hermesc/win64-bin/vcruntime140.dll +0 -0
  31. package/sdks/hermesc/win64-bin/vcruntime140_1.dll +0 -0
  32. package/template/package.json +1 -1
@@ -348,7 +348,9 @@ class TouchableHighlight extends React.Component<Props, State> {
348
348
  onLayout={this.props.onLayout}
349
349
  hitSlop={this.props.hitSlop}
350
350
  hasTVPreferredFocus={this.props.hasTVPreferredFocus === true}
351
- isTVSelectable={this.props.isTVSelectable !== false && this.props.accessible !== false}
351
+ isTVSelectable={
352
+ this.props.isTVSelectable !== false && this.props.accessible !== false
353
+ }
352
354
  tvParallaxProperties={this.props.tvParallaxProperties}
353
355
  nextFocusDown={tagForComponentOrHandle(this.props.nextFocusDown)}
354
356
  nextFocusForward={tagForComponentOrHandle(this.props.nextFocusForward)}
@@ -376,6 +378,7 @@ class TouchableHighlight extends React.Component<Props, State> {
376
378
  }
377
379
 
378
380
  componentDidMount(): void {
381
+ this.state.pressability.configure(this._createPressabilityConfig());
379
382
  this._isMounted = true;
380
383
  if (Platform.isTV) {
381
384
  this._tvTouchable = new TVTouchable(this, {
@@ -340,6 +340,7 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
340
340
  }
341
341
 
342
342
  componentDidMount(): void {
343
+ this.state.pressability.configure(this._createPressabilityConfig());
343
344
  if (Platform.isTV) {
344
345
  this._tvTouchable = new TVTouchable(this, {
345
346
  getDisabled: () => this.props.disabled === true,
@@ -371,10 +372,6 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
371
372
  this.state.pressability.configure(this._createPressabilityConfig());
372
373
  }
373
374
 
374
- componentDidMount(): mixed {
375
- this.state.pressability.configure(this._createPressabilityConfig());
376
- }
377
-
378
375
  componentWillUnmount(): void {
379
376
  if (Platform.isTV) {
380
377
  if (this._tvTouchable != null) {
@@ -312,6 +312,7 @@ class TouchableOpacity extends React.Component<Props, State> {
312
312
  }
313
313
 
314
314
  componentDidMount(): void {
315
+ this.state.pressability.configure(this._createPressabilityConfig());
315
316
  if (Platform.isTV) {
316
317
  this._tvTouchable = new TVTouchable(this, {
317
318
  getDisabled: () => this.props.disabled === true,
@@ -356,10 +357,6 @@ class TouchableOpacity extends React.Component<Props, State> {
356
357
  }
357
358
  }
358
359
 
359
- componentDidMount(): void {
360
- this.state.pressability.configure(this._createPressabilityConfig());
361
- }
362
-
363
360
  componentWillUnmount(): void {
364
361
  if (Platform.isTV) {
365
362
  if (this._tvTouchable != null) {
@@ -192,6 +192,7 @@ class TouchableWithoutFeedback extends React.Component<Props, State> {
192
192
  }
193
193
 
194
194
  componentDidMount(): void {
195
+ this.state.pressability.configure(createPressabilityConfig(this.props));
195
196
  if (Platform.isTV) {
196
197
  this._tvTouchable = new TVTouchable(this, {
197
198
  getDisabled: () => this.props.disabled === true,
@@ -223,10 +224,6 @@ class TouchableWithoutFeedback extends React.Component<Props, State> {
223
224
  this.state.pressability.configure(createPressabilityConfig(this.props));
224
225
  }
225
226
 
226
- componentDidMount(): mixed {
227
- this.state.pressability.configure(createPressabilityConfig(this.props));
228
- }
229
-
230
227
  componentWillUnmount(): void {
231
228
  if (Platform.isTV) {
232
229
  if (this._tvTouchable != null) {
@@ -12,6 +12,6 @@
12
12
  exports.version = {
13
13
  major: 0,
14
14
  minor: 73,
15
- patch: 4,
16
- prerelease: '0rc0',
15
+ patch: 6,
16
+ prerelease: '0',
17
17
  };
@@ -192,7 +192,7 @@ export function parseComponentStack(message: string): ComponentStack {
192
192
  if (!s) {
193
193
  return null;
194
194
  }
195
- const match = s.match(/(.*) \(at (.*\.js):([\d]+)\)/);
195
+ const match = s.match(/(.*) \(at (.*\.(?:js|jsx|ts|tsx)):([\d]+)\)/);
196
196
  if (!match) {
197
197
  return null;
198
198
  }
@@ -158,6 +158,8 @@
158
158
  [attributedText insertAttributedString:propertyAttributedText atIndex:0];
159
159
  }
160
160
 
161
+ [self postprocessAttributedText:attributedText];
162
+
161
163
  NSAttributedString *newAttributedText;
162
164
  if (![_previousAttributedText isEqualToAttributedString:attributedText]) {
163
165
  // We have to follow `set prop` pattern:
@@ -191,6 +193,52 @@
191
193
  }];
192
194
  }
193
195
 
196
+ - (void)postprocessAttributedText:(NSMutableAttributedString *)attributedText
197
+ {
198
+ __block CGFloat maximumLineHeight = 0;
199
+
200
+ [attributedText enumerateAttribute:NSParagraphStyleAttributeName
201
+ inRange:NSMakeRange(0, attributedText.length)
202
+ options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
203
+ usingBlock:^(NSParagraphStyle *paragraphStyle, __unused NSRange range, __unused BOOL *stop) {
204
+ if (!paragraphStyle) {
205
+ return;
206
+ }
207
+
208
+ maximumLineHeight = MAX(paragraphStyle.maximumLineHeight, maximumLineHeight);
209
+ }];
210
+
211
+ if (maximumLineHeight == 0) {
212
+ // `lineHeight` was not specified, nothing to do.
213
+ return;
214
+ }
215
+
216
+ __block CGFloat maximumFontLineHeight = 0;
217
+
218
+ [attributedText enumerateAttribute:NSFontAttributeName
219
+ inRange:NSMakeRange(0, attributedText.length)
220
+ options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
221
+ usingBlock:^(UIFont *font, NSRange range, __unused BOOL *stop) {
222
+ if (!font) {
223
+ return;
224
+ }
225
+
226
+ if (maximumFontLineHeight <= font.lineHeight) {
227
+ maximumFontLineHeight = font.lineHeight;
228
+ }
229
+ }];
230
+
231
+ if (maximumLineHeight < maximumFontLineHeight) {
232
+ return;
233
+ }
234
+
235
+ CGFloat baseLineOffset = maximumLineHeight / 2.0 - maximumFontLineHeight / 2.0;
236
+
237
+ [attributedText addAttribute:NSBaselineOffsetAttributeName
238
+ value:@(baseLineOffset)
239
+ range:NSMakeRange(0, attributedText.length)];
240
+ }
241
+
194
242
  #pragma mark -
195
243
 
196
244
  - (NSAttributedString *)measurableAttributedText
package/README.md CHANGED
@@ -59,12 +59,12 @@ yarn create react-native-app TestApp --template https://github.com/react-native-
59
59
  cd TestApp
60
60
  # Now build and start the app in the tvOS Simulator - this will only work on a macOS machine.
61
61
  # This command can also be run via "yarn tvos".
62
- npx expo run:ios --scheme MyApp-tvOS --device "Apple TV"
62
+ npx expo run:ios --scheme TestApp-tvOS --device "Apple TV"
63
63
  # You can also build and start the app on an iOS phone simulator.
64
64
  # This command can also be run via "yarn ios".
65
65
  npx expo run:ios
66
66
  # or specify a simulator:
67
- npx expo run:ios --scheme MyApp --device "iPhone 15"
67
+ npx expo run:ios --scheme TestApp --device "iPhone 15"
68
68
  # This command builds and starts the app in an Android TV emulator or a phone emulator (needs to be created in advance).
69
69
  # This command can also be run via "yarn android".
70
70
  npx expo run:android --device tv_api_31
@@ -204,15 +204,24 @@ class Game2048 extends React.Component {
204
204
 
205
205
  - _TVFocusGuideView_: This component provides support for Apple's `UIFocusGuide` API and is implemented in the same way for Android TV, to help ensure that focusable controls can be navigated to, even if they are not directly in line with other controls. An example is provided in `RNTester` that shows two different ways of using this component.
206
206
 
207
- | Prop | Value | Description |
208
- |---|---|---|
209
- | destinations | any[]? | Array of `Component`s to register as destinations of the FocusGuideView |
210
- | autoFocus | boolean? | If true, `TVFocusGuide` will automatically manage focus for you. It will redirect the focus to the first focusable child on the first visit. It also remembers the last focused child and redirects the focus to it on the subsequent visits. `destinations` prop takes precedence over this prop when used together. |
211
- | trapFocus* (Up, Down, Left, Right) | Prevents focus escaping from the container for the given directions. |
212
-
213
- More information on the focus handling improvements above can be found in [this article](https://medium.com/xite-engineering/revolutionizing-focus-management-in-tv-applications-with-react-native-10ba69bd90).
214
-
215
- - _Next Focus Direction_: the props `nextFocus*` on `View` should work as expected on iOS too (previously android only). One caveat is that if there is no focusable in the `nextFocusable*` direction next to the starting view, iOS doesn't check if we want to override the destination.
216
-
217
- - _TVTextScrollView_: On Apple TV, a ScrollView will not scroll unless there are focusable items inside it or above/below it. This component wraps ScrollView and uses tvOS-specific native code to allow scrolling using swipe gestures from the remote control.
218
-
207
+ | Prop | Value | Description |
208
+ |---|---|---|
209
+ | destinations | any[]? | Array of `Component`s to register as destinations of the FocusGuideView |
210
+ | autoFocus | boolean? | If true, `TVFocusGuide` will automatically manage focus for you. It will redirect the focus to the first focusable child on the first visit. It also remembers the last focused child and redirects the focus to it on the subsequent visits. `destinations` prop takes precedence over this prop when used together. |
211
+ | trapFocus* (Up, Down, Left, Right) | Prevents focus escaping from the container for the given directions. |
212
+
213
+ More information on the focus handling improvements above can be found in [this article](https://medium.com/xite-engineering/revolutionizing-focus-management-in-tv-applications-with-react-native-10ba69bd90).
214
+
215
+ - _Next Focus Direction_: the props `nextFocus*` on `View` should work as expected on iOS too (previously android only). One caveat is that if there is no focusable in the `nextFocusable*` direction next to the starting view, iOS doesn't check if we want to override the destination.
216
+
217
+ - _TVTextScrollView_: On Apple TV, a ScrollView will not scroll unless there are focusable items inside it or above/below it. This component wraps ScrollView and uses tvOS-specific native code to allow scrolling using swipe gestures from the remote control.
218
+
219
+ - VirtualizedList: We extend `VirtualizedList` to make virtualization work well with focus management in mind. All of the improvements that we made are automatically available to all the VirtualizedList based components such as `FlatList`.
220
+ - Defaults
221
+ - VirtualizeList contents are automatically wrapped with a `TVFocusGuideView` with `trapFocus*` properties enabled depending on the orientation of the list. This default makes sure that focus doesn't leave the list accidentally due to a virtualization issue etc. until reaching the beginning or the end of the list.
222
+
223
+ New Props:
224
+
225
+ | Prop | Value | Description |
226
+ |---|---|---|
227
+ | additionalRenderRegions | {first: number; last: number;}[]? | Array of `RenderRegions` that allows you to define regions in the list that are not subject to virtualization, ensuring they are always rendered. This is particularly useful for preventing blank areas in critical parts of the list. These regions are rendered lazily after the initial render and are specified as an array of objects, each with `first` and `last` indices marking the beginning and end of the non-virtualized region based on index. See the [feature proposal](https://github.com/react-native-tvos/react-native-tvos/discussions/663) for more context. |
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
23
23
  __rnVersion = @{
24
24
  RCTVersionMajor: @(0),
25
25
  RCTVersionMinor: @(73),
26
- RCTVersionPatch: @(4),
27
- RCTVersionPrerelease: @"0rc0",
26
+ RCTVersionPatch: @(6),
27
+ RCTVersionPrerelease: @"0",
28
28
  };
29
29
  });
30
30
  return __rnVersion;
@@ -24,9 +24,14 @@
24
24
  #import "RCTEnhancedScrollView.h"
25
25
  #import "RCTFabricComponentsPlugins.h"
26
26
 
27
+ #if TARGET_OS_TV
28
+ #import "RCTTVRemoteHandler.h"
29
+ #endif
30
+
27
31
  using namespace facebook::react;
28
32
 
29
33
  static const CGFloat kClippingLeeway = 44.0;
34
+ static const float TV_DEFAULT_SWIPE_DURATION = 0.3;
30
35
 
31
36
  static UIScrollViewKeyboardDismissMode RCTUIKeyboardDismissModeFromProps(const ScrollViewProps &props)
32
37
  {
@@ -105,6 +110,9 @@ static void RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrol
105
110
 
106
111
  CGRect _prevFirstVisibleFrame;
107
112
  __weak UIView *_firstVisibleView;
113
+
114
+ BOOL _blockFirstTouch;
115
+ NSMutableDictionary *_tvRemoteGestureRecognizers;
108
116
  }
109
117
 
110
118
  + (RCTScrollViewComponentView *_Nullable)findScrollViewComponentViewForView:(UIView *)view
@@ -139,6 +147,8 @@ static void RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrol
139
147
  } else {
140
148
  _scrollEventThrottle = INFINITY;
141
149
  }
150
+
151
+ _tvRemoteGestureRecognizers = [NSMutableDictionary new];
142
152
  }
143
153
 
144
154
  return self;
@@ -792,6 +802,242 @@ static void RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrol
792
802
  }
793
803
  }
794
804
 
805
+ #pragma mark Apple TV swipe and focus handling
806
+
807
+ #if TARGET_OS_TV
808
+ - (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context
809
+ withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
810
+ {
811
+ if (context.previouslyFocusedView == context.nextFocusedView || !_props->isTVSelectable) {
812
+ return;
813
+ }
814
+ if (context.nextFocusedView == self) {
815
+ [self becomeFirstResponder];
816
+ [self addSwipeGestureRecognizers];
817
+ [self sendFocusNotification];
818
+ // if we enter the scroll view from different view then block first touch event since it is the event that triggered the focus
819
+ _blockFirstTouch = (unsigned long)context.focusHeading != 0;
820
+ [self addArrowsListeners];
821
+ } else if (context.previouslyFocusedView == self) {
822
+ [self removeArrowsListeners];
823
+ [self sendBlurNotification];
824
+ [self removeSwipeGestureRecognizers];
825
+ [self resignFirstResponder];
826
+ // if we leave the scroll view and go up, then scroll to top; if going down,
827
+ // scroll to bottom
828
+ // Similarly for left and right
829
+ RCTEnhancedScrollView *scrollView = (RCTEnhancedScrollView *)_scrollView;
830
+ if (context.focusHeading == UIFocusHeadingUp && scrollView.snapToStart) {
831
+ [self swipeVerticalScrollToOffset:0.0];
832
+ } else if(context.focusHeading == UIFocusHeadingDown && scrollView.snapToEnd) {
833
+ [self swipeVerticalScrollToOffset:scrollView.contentSize.height];
834
+ } else if(context.focusHeading == UIFocusHeadingLeft && scrollView.snapToStart) {
835
+ [self swipeHorizontalScrollToOffset:0.0];
836
+ } else if(context.focusHeading == UIFocusHeadingRight && scrollView.snapToEnd) {
837
+ [self swipeHorizontalScrollToOffset:scrollView.contentSize.width];
838
+ }
839
+ }
840
+ }
841
+
842
+ - (void)addArrowsListeners
843
+ {
844
+ [[NSNotificationCenter defaultCenter] addObserver:self
845
+ selector:@selector(handleTVNavigationEventNotification:)
846
+ name:@"RCTTVNavigationEventNotification"
847
+ object:nil];
848
+ }
849
+
850
+ - (void)removeArrowsListeners
851
+ {
852
+ [[NSNotificationCenter defaultCenter] removeObserver:self
853
+ name:@"RCTTVNavigationEventNotification"
854
+ object:nil];
855
+ }
856
+
857
+ - (void)handleTVNavigationEventNotification:(NSNotification *)notif
858
+ {
859
+ NSArray *supportedEvents = [NSArray arrayWithObjects:@"up", @"down", @"left", @"right", nil];
860
+
861
+ if (notif.object == nil || notif.object[@"eventType"] == nil || ![supportedEvents containsObject:notif.object[@"eventType"]] ) {
862
+ return;
863
+ }
864
+
865
+ if (_blockFirstTouch) {
866
+ _blockFirstTouch = NO;
867
+ return;
868
+ }
869
+
870
+ BOOL isHorizontal = _scrollView.contentSize.width > self.frame.size.width;
871
+ if (!isHorizontal) {
872
+ if ([notif.object[@"eventType"] isEqual: @"down"]) {
873
+ [self swipedDown];
874
+ return;
875
+ }
876
+
877
+ if ([notif.object[@"eventType"] isEqual: @"up"]) {
878
+ [self swipedUp];
879
+ return;
880
+ }
881
+ }
882
+
883
+ if ([notif.object[@"eventType"] isEqual: @"left"]) {
884
+ [self swipedLeft];
885
+ return;
886
+ }
887
+
888
+ if ([notif.object[@"eventType"] isEqual: @"right"]) {
889
+ [self swipedRight];
890
+ return;
891
+ }
892
+ }
893
+
894
+ - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
895
+ {
896
+ BOOL isHorizontal = _scrollView.contentSize.width > self.frame.size.width;
897
+ // Keep focus inside the scroll view till the end of the content
898
+ if (isHorizontal) {
899
+ if ((context.focusHeading == UIFocusHeadingLeft && self.scrollView.contentOffset.x > 0)
900
+ || (context.focusHeading == UIFocusHeadingRight && self.scrollView.contentOffset.x < self.scrollView.contentSize.width - self.scrollView.visibleSize.width)
901
+ ) {
902
+ return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
903
+ }
904
+ } else {
905
+ if ((context.focusHeading == UIFocusHeadingUp && self.scrollView.contentOffset.y > 0)
906
+ || (context.focusHeading == UIFocusHeadingDown && self.scrollView.contentOffset.y < self.scrollView.contentSize.height - self.scrollView.visibleSize.height)
907
+ ) {
908
+ return [UIFocusSystem environment:self containsEnvironment:context.nextFocusedItem];
909
+ }
910
+ }
911
+
912
+ return [super shouldUpdateFocusInContext:context];
913
+ }
914
+
915
+ - (void)sendFocusNotification
916
+ {
917
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification"
918
+ object:@{ @"eventType": @"focus", @"tag": @([self tag]) }];
919
+ }
920
+
921
+ - (void)sendBlurNotification
922
+ {
923
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTTVNavigationEventNotification"
924
+ object:@{ @"eventType": @"blur", @"tag": @([self tag]) }];
925
+ }
926
+
927
+ - (NSInteger)swipeVerticalInterval
928
+ {
929
+ RCTEnhancedScrollView *scrollView = (RCTEnhancedScrollView *)_scrollView;
930
+ if (scrollView.snapToInterval) {
931
+ return scrollView.snapToInterval;
932
+ }
933
+ return scrollView.visibleSize.height / 2;
934
+ }
935
+
936
+ - (NSInteger)swipeHorizontalInterval
937
+ {
938
+ RCTEnhancedScrollView *scrollView = (RCTEnhancedScrollView *)_scrollView;
939
+ if (scrollView.snapToInterval) {
940
+ return scrollView.snapToInterval;
941
+ }
942
+ return scrollView.visibleSize.width / 2;
943
+ }
944
+
945
+ - (NSTimeInterval)swipeDuration
946
+ {
947
+ auto pressDuration = _props->tvParallaxProperties.pressDuration;
948
+ auto duration = pressDuration.has_value() ? pressDuration.value() : TV_DEFAULT_SWIPE_DURATION;
949
+ if (duration == 0.0) {
950
+ duration = TV_DEFAULT_SWIPE_DURATION;
951
+ }
952
+ return duration;
953
+ }
954
+
955
+ - (void)swipeVerticalScrollToOffset:(CGFloat)yOffset
956
+ {
957
+ _blockFirstTouch = NO;
958
+ dispatch_async(dispatch_get_main_queue(), ^{
959
+ CGFloat limitedOffset = yOffset;
960
+ limitedOffset = MAX(limitedOffset, 0.0);
961
+ limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.height - self.scrollView.visibleSize.height);
962
+ [UIView animateWithDuration:[self swipeDuration] animations:^{
963
+ self.scrollView.contentOffset =
964
+ CGPointMake(self.scrollView.contentOffset.x, limitedOffset);
965
+ }];
966
+ });
967
+ }
968
+
969
+ - (void)swipeHorizontalScrollToOffset:(CGFloat)xOffset
970
+ {
971
+ _blockFirstTouch = NO;
972
+ dispatch_async(dispatch_get_main_queue(), ^{
973
+ CGFloat limitedOffset = xOffset;
974
+ limitedOffset = MAX(limitedOffset, 0.0);
975
+ limitedOffset = MIN(limitedOffset, self.scrollView.contentSize.width - self.scrollView.visibleSize.width);
976
+ [UIView animateWithDuration:[self swipeDuration] animations:^{
977
+ self.scrollView.contentOffset =
978
+ CGPointMake(limitedOffset, self.scrollView.contentOffset.y);
979
+ }];
980
+ });
981
+ }
982
+
983
+ - (void)swipedUp
984
+ {
985
+ CGFloat newOffset = self.scrollView.contentOffset.y - [self swipeVerticalInterval];
986
+ NSLog(@"Swiped up to %f", newOffset);
987
+ [self swipeVerticalScrollToOffset:newOffset];
988
+ }
989
+
990
+ - (void)swipedDown
991
+ {
992
+ CGFloat newOffset = self.scrollView.contentOffset.y + [self swipeVerticalInterval];
993
+ NSLog(@"Swiped down to %f", newOffset);
994
+ [self swipeVerticalScrollToOffset:newOffset];
995
+ }
996
+
997
+ - (void)swipedLeft
998
+ {
999
+ CGFloat newOffset = self.scrollView.contentOffset.x - [self swipeHorizontalInterval];
1000
+ NSLog(@"Swiped left to %f", newOffset);
1001
+ [self swipeHorizontalScrollToOffset:newOffset];
1002
+ }
1003
+
1004
+ - (void)swipedRight
1005
+ {
1006
+ CGFloat newOffset = self.scrollView.contentOffset.x + [self swipeHorizontalInterval];
1007
+ NSLog(@"Swiped right to %f", newOffset);
1008
+ [self swipeHorizontalScrollToOffset:newOffset];
1009
+ }
1010
+
1011
+ - (void)addSwipeGestureRecognizers
1012
+ {
1013
+ [self addSwipeGestureRecognizerWithSelector:@selector(swipedUp) direction:UISwipeGestureRecognizerDirectionUp name:RCTTVRemoteEventSwipeUp];
1014
+ [self addSwipeGestureRecognizerWithSelector:@selector(swipedDown) direction:UISwipeGestureRecognizerDirectionDown name:RCTTVRemoteEventSwipeDown];
1015
+ [self addSwipeGestureRecognizerWithSelector:@selector(swipedLeft) direction:UISwipeGestureRecognizerDirectionLeft name:RCTTVRemoteEventSwipeLeft];
1016
+ [self addSwipeGestureRecognizerWithSelector:@selector(swipedRight) direction:UISwipeGestureRecognizerDirectionRight name:RCTTVRemoteEventSwipeRight];
1017
+ }
1018
+
1019
+ - (void)addSwipeGestureRecognizerWithSelector:(nonnull SEL)selector
1020
+ direction:(UISwipeGestureRecognizerDirection)direction
1021
+ name:(NSString *)name
1022
+ {
1023
+ UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:selector];
1024
+ recognizer.direction = direction;
1025
+
1026
+ _tvRemoteGestureRecognizers[name] = recognizer;
1027
+ [self.scrollView addGestureRecognizer:recognizer];
1028
+ }
1029
+
1030
+ - (void)removeSwipeGestureRecognizers
1031
+ {
1032
+ NSArray *names = [self->_tvRemoteGestureRecognizers allKeys];
1033
+ for (NSString *name in names) {
1034
+ UIGestureRecognizer *r = self->_tvRemoteGestureRecognizers[name];
1035
+ [self.scrollView removeGestureRecognizer:r];
1036
+ [self->_tvRemoteGestureRecognizers removeObjectForKey:name];
1037
+ }
1038
+ }
1039
+ #endif // TARGET_OS_TV
1040
+
795
1041
  @end
796
1042
 
797
1043
  Class<RCTComponentViewProtocol> RCTScrollViewCls(void)
@@ -45,7 +45,7 @@ using namespace facebook::react;
45
45
 
46
46
  @implementation RCTViewComponentView {
47
47
  UIColor *_backgroundColor;
48
- CALayer *_borderLayer;
48
+ __weak CALayer *_borderLayer;
49
49
  BOOL _needsInvalidateLayer;
50
50
  BOOL _isJSResponder;
51
51
  BOOL _removeClippedSubviews;
@@ -433,6 +433,9 @@ using namespace facebook::react;
433
433
  //
434
434
  - (void)enableDirectionalFocusGuides
435
435
  {
436
+ if (!self.isFocused) {
437
+ return;
438
+ }
436
439
  if (self->_nextFocusUp != nil) {
437
440
  if (self.focusGuideUp == nil) {
438
441
  self.focusGuideUp = [UIFocusGuide new];
@@ -942,6 +945,7 @@ using namespace facebook::react;
942
945
  if (newViewProps.nextFocusUp.has_value()) {
943
946
  UIView *rootView = [self containingRootView];
944
947
  _nextFocusUp = [rootView viewWithTag:newViewProps.nextFocusUp.value()];
948
+ [self enableDirectionalFocusGuides];
945
949
  } else {
946
950
  _nextFocusUp = nil;
947
951
  }
@@ -951,6 +955,7 @@ using namespace facebook::react;
951
955
  if (newViewProps.nextFocusDown.has_value()) {
952
956
  UIView *rootView = [self containingRootView];
953
957
  _nextFocusDown = [rootView viewWithTag:newViewProps.nextFocusDown.value()];
958
+ [self enableDirectionalFocusGuides];
954
959
  } else {
955
960
  _nextFocusDown = nil;
956
961
  }
@@ -960,6 +965,7 @@ using namespace facebook::react;
960
965
  if (newViewProps.nextFocusLeft.has_value()) {
961
966
  UIView *rootView = [self containingRootView];
962
967
  _nextFocusLeft = [rootView viewWithTag:newViewProps.nextFocusLeft.value()];
968
+ [self enableDirectionalFocusGuides];
963
969
  } else {
964
970
  _nextFocusLeft = nil;
965
971
  }
@@ -969,6 +975,7 @@ using namespace facebook::react;
969
975
  if (newViewProps.nextFocusRight.has_value()) {
970
976
  UIView *rootView = [self containingRootView];
971
977
  _nextFocusRight = [rootView viewWithTag:newViewProps.nextFocusRight.value()];
978
+ [self enableDirectionalFocusGuides];
972
979
  } else {
973
980
  _nextFocusRight = nil;
974
981
  }
@@ -1013,9 +1020,7 @@ using namespace facebook::react;
1013
1020
  _layoutMetrics = layoutMetrics;
1014
1021
  _needsInvalidateLayer = YES;
1015
1022
 
1016
- if (_borderLayer) {
1017
- _borderLayer.frame = self.layer.bounds;
1018
- }
1023
+ _borderLayer.frame = self.layer.bounds;
1019
1024
 
1020
1025
  if (_contentView) {
1021
1026
  _contentView.frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());
@@ -1232,10 +1237,7 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
1232
1237
 
1233
1238
  if (useCoreAnimationBorderRendering) {
1234
1239
  layer.mask = nil;
1235
- if (_borderLayer) {
1236
- [_borderLayer removeFromSuperlayer];
1237
- _borderLayer = nil;
1238
- }
1240
+ [_borderLayer removeFromSuperlayer];
1239
1241
 
1240
1242
  layer.borderWidth = (CGFloat)borderMetrics.borderWidths.left;
1241
1243
  CGColorRef borderColor = RCTCreateCGColorRefFromSharedColor(borderMetrics.borderColors.left);
@@ -1248,11 +1250,12 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
1248
1250
  layer.backgroundColor = _backgroundColor.CGColor;
1249
1251
  } else {
1250
1252
  if (!_borderLayer) {
1251
- _borderLayer = [CALayer new];
1252
- _borderLayer.zPosition = -1024.0f;
1253
- _borderLayer.frame = layer.bounds;
1254
- _borderLayer.magnificationFilter = kCAFilterNearest;
1255
- [layer addSublayer:_borderLayer];
1253
+ CALayer *borderLayer = [CALayer new];
1254
+ borderLayer.zPosition = -1024.0f;
1255
+ borderLayer.frame = layer.bounds;
1256
+ borderLayer.magnificationFilter = kCAFilterNearest;
1257
+ [layer addSublayer:borderLayer];
1258
+ _borderLayer = borderLayer;
1256
1259
  }
1257
1260
 
1258
1261
  layer.backgroundColor = nil;
@@ -1293,6 +1296,10 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
1293
1296
  }
1294
1297
  }
1295
1298
 
1299
+ // If mutations are applied inside of Animation block, it may cause _borderLayer to be animated.
1300
+ // To stop that, imperatively remove all animations from _borderLayer.
1301
+ [_borderLayer removeAllAnimations];
1302
+
1296
1303
  // Stage 2.5. Custom Clipping Mask
1297
1304
  CAShapeLayer *maskLayer = nil;
1298
1305
  CGFloat cornerRadius = 0;
@@ -20,6 +20,7 @@ folly_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_C
20
20
  folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32'
21
21
  folly_version = '2022.05.16.00'
22
22
  boost_compiler_flags = '-Wno-documentation'
23
+ new_arch_flags = ENV['RCT_NEW_ARCH_ENABLED'] == '1' ? ' -DRCT_NEW_ARCH_ENABLED=1' : ''
23
24
 
24
25
  header_search_paths = [
25
26
  "\"$(PODS_TARGET_SRCROOT)/ReactCommon\"",
@@ -56,13 +57,13 @@ Pod::Spec.new do |s|
56
57
  "Fabric/**/RCTSurfacePointerHandler.mm"
57
58
  s.ios.exclude_files = "Fabric/**/RCTSurfaceTouchHandlerTV.mm",
58
59
  "Fabric/**/RCTSurfacePointerHandlerTV.mm"
59
- s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
60
+ s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags + new_arch_flags
60
61
  s.header_dir = header_dir
61
62
  s.module_name = module_name
62
63
  s.framework = ["JavaScriptCore", "MobileCoreServices"]
63
64
  s.pod_target_xcconfig = {
64
65
  "HEADER_SEARCH_PATHS" => header_search_paths,
65
- "OTHER_CFLAGS" => "$(inherited) -DRN_FABRIC_ENABLED" + " " + folly_flags,
66
+ "OTHER_CFLAGS" => "$(inherited) -DRN_FABRIC_ENABLED" + " " + folly_flags + new_arch_flags,
66
67
  "CLANG_CXX_LANGUAGE_STANDARD" => "c++20"
67
68
  }.merge!(ENV['USE_FRAMEWORKS'] != nil ? {
68
69
  "PUBLIC_HEADERS_FOLDER_PATH" => "#{module_name}.framework/Headers/#{header_dir}"
@@ -336,6 +336,9 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
336
336
  //
337
337
  - (void)enableDirectionalFocusGuides
338
338
  {
339
+ if (!self.isFocused) {
340
+ return;
341
+ }
339
342
  if (self->_nextFocusUp != nil) {
340
343
  if (self.focusGuideUp == nil) {
341
344
  self.focusGuideUp = [UIFocusGuide new];
@@ -455,18 +458,22 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
455
458
 
456
459
  - (void)setNextFocusUp:(NSNumber *)nextFocusUp {
457
460
  self->_nextFocusUp = [self getViewById: nextFocusUp];
461
+ [self enableDirectionalFocusGuides];
458
462
  }
459
463
 
460
464
  - (void)setNextFocusDown:(NSNumber *)nextFocusDown {
461
465
  self->_nextFocusDown = [self getViewById: nextFocusDown];
466
+ [self enableDirectionalFocusGuides];
462
467
  }
463
468
 
464
469
  - (void)setNextFocusLeft:(NSNumber *)nextFocusLeft {
465
470
  self->_nextFocusLeft = [self getViewById: nextFocusLeft];
471
+ [self enableDirectionalFocusGuides];
466
472
  }
467
473
 
468
474
  - (void)setNextFocusRight:(NSNumber *)nextFocusRight {
469
475
  self->_nextFocusRight = [self getViewById: nextFocusRight];
476
+ [self enableDirectionalFocusGuides];
470
477
  }
471
478
 
472
479
  - (void)setPreferredFocus:(BOOL)hasTVPreferredFocus
@@ -1,4 +1,4 @@
1
- VERSION_NAME=0.73.4-0rc0
1
+ VERSION_NAME=0.73.6-0
2
2
 
3
3
  # react.internal.publishingGroup=com.facebook.react
4
4
  # For TV use this group
@@ -17,6 +17,6 @@ public class ReactNativeVersion {
17
17
  public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
18
18
  "major", 0,
19
19
  "minor", 73,
20
- "patch", 4,
21
- "prerelease", "0rc0");
20
+ "patch", 6,
21
+ "prerelease", "0");
22
22
  }
@@ -17,8 +17,8 @@ namespace facebook::react {
17
17
  constexpr struct {
18
18
  int32_t Major = 0;
19
19
  int32_t Minor = 73;
20
- int32_t Patch = 4;
21
- std::string_view Prerelease = "0rc0";
20
+ int32_t Patch = 6;
21
+ std::string_view Prerelease = "0";
22
22
  } ReactNativeVersion;
23
23
 
24
24
  } // namespace facebook::react
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-tvos",
3
- "version": "0.73.4-0rc0",
3
+ "version": "0.73.6-0",
4
4
  "description": "A framework for building native apps using React",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -94,16 +94,16 @@
94
94
  },
95
95
  "dependencies": {
96
96
  "@jest/create-cache-key-function": "^29.6.3",
97
- "@react-native-community/cli": "12.3.2",
98
- "@react-native-community/cli-platform-android": "12.3.2",
99
- "@react-native-community/cli-platform-ios": "12.3.2",
97
+ "@react-native-community/cli": "12.3.6",
98
+ "@react-native-community/cli-platform-android": "12.3.6",
99
+ "@react-native-community/cli-platform-ios": "12.3.6",
100
100
  "@react-native/assets-registry": "0.73.1",
101
- "@react-native/community-cli-plugin": "0.73.16",
101
+ "@react-native/community-cli-plugin": "0.73.17",
102
102
  "@react-native/codegen": "0.73.3",
103
103
  "@react-native/gradle-plugin": "0.73.4",
104
104
  "@react-native/js-polyfills": "0.73.1",
105
105
  "@react-native/normalize-colors": "0.73.2",
106
- "@react-native-tvos/virtualized-lists": "0.73.4",
106
+ "@react-native-tvos/virtualized-lists": "0.73.6-0",
107
107
  "abort-controller": "^3.0.0",
108
108
  "anser": "^1.4.9",
109
109
  "ansi-regex": "^5.0.0",
@@ -151,6 +151,6 @@
151
151
  ]
152
152
  },
153
153
  "devDependencies": {
154
- "react-native-core": "npm:react-native@0.73.4"
154
+ "react-native-core": "npm:react-native@0.73.6"
155
155
  }
156
156
  }
@@ -104,6 +104,10 @@ class NewArchitectureHelper
104
104
  current_config = hash["pod_target_xcconfig"] != nil ? hash["pod_target_xcconfig"] : {}
105
105
  current_headers = current_config["HEADER_SEARCH_PATHS"] != nil ? current_config["HEADER_SEARCH_PATHS"] : ""
106
106
 
107
+ flags_to_add = new_arch_enabled ?
108
+ "#{@@folly_compiler_flags} -DRCT_NEW_ARCH_ENABLED=1" :
109
+ "#{@@folly_compiler_flags}"
110
+
107
111
  header_search_paths = ["\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/Headers/Private/Yoga\""]
108
112
  if ENV['USE_FRAMEWORKS']
109
113
  header_search_paths << "\"$(PODS_ROOT)/DoubleConversion\""
@@ -123,7 +127,7 @@ class NewArchitectureHelper
123
127
  }
124
128
  end
125
129
  header_search_paths_string = header_search_paths.join(" ")
126
- spec.compiler_flags = compiler_flags.empty? ? @@folly_compiler_flags : "#{compiler_flags} #{@@folly_compiler_flags}"
130
+ spec.compiler_flags = compiler_flags.empty? ? "$(inherited) #{flags_to_add}" : "$(inherited) #{compiler_flags} #{flags_to_add}"
127
131
  current_config["HEADER_SEARCH_PATHS"] = current_headers.empty? ?
128
132
  header_search_paths_string :
129
133
  "#{current_headers} #{header_search_paths_string}"
@@ -86,6 +86,27 @@ class ReactNativePodsUtils
86
86
  end
87
87
  end
88
88
 
89
+ def self.fix_flipper_for_xcode_15_3(installer)
90
+ installer.pods_project.targets.each do |target|
91
+ if target.name == 'Flipper'
92
+ file_path = 'Pods/Flipper/xplat/Flipper/FlipperTransportTypes.h'
93
+ if !File.exist?(file_path)
94
+ return
95
+ end
96
+
97
+ contents = File.read(file_path)
98
+ if contents.include?('#include <functional>')
99
+ return
100
+ end
101
+ mod_content = contents.gsub("#pragma once", "#pragma once\n#include <functional>")
102
+ File.chmod(0755, file_path)
103
+ File.open(file_path, 'w') do |file|
104
+ file.puts(mod_content)
105
+ end
106
+ end
107
+ end
108
+ end
109
+
89
110
  def self.set_use_hermes_build_setting(installer, hermes_enabled)
90
111
  Pod::UI.puts("Setting USE_HERMES build settings")
91
112
  projects = self.extract_projects(installer)
@@ -32,7 +32,13 @@ const REACT_NATIVE_PACKAGE_ROOT_FOLDER = path.join(__dirname, '..', '..');
32
32
 
33
33
  const CODEGEN_DEPENDENCY_NAME = '@react-native/codegen';
34
34
  const CODEGEN_REPO_PATH = `${REACT_NATIVE_REPOSITORY_ROOT}/packages/react-native-codegen`;
35
- const CODEGEN_NPM_PATH = `${REACT_NATIVE_PACKAGE_ROOT_FOLDER}/../${CODEGEN_DEPENDENCY_NAME}`;
35
+ // This is a change for 0.73-stable only since this piece of code was replaced:
36
+ // https://github.com/facebook/react-native/commit/9071a3a0b0e11ad711927651bcb2412f553b6fe9
37
+ const CODEGEN_NPM_PATH = path.dirname(
38
+ require.resolve(path.join(CODEGEN_DEPENDENCY_NAME, 'package.json'), {
39
+ paths: [REACT_NATIVE_PACKAGE_ROOT_FOLDER],
40
+ }),
41
+ );
36
42
  const CORE_LIBRARIES_WITH_OUTPUT_FOLDER = {
37
43
  rncore: path.join(REACT_NATIVE_PACKAGE_ROOT_FOLDER, 'ReactCommon'),
38
44
  FBReactNativeSpec: null,
@@ -189,33 +195,34 @@ function handleThirdPartyLibraries(
189
195
  codegenConfigKey,
190
196
  ) {
191
197
  // Determine which of these are codegen-enabled libraries
192
- const configDir =
193
- baseCodegenConfigFileDir ||
194
- path.join(REACT_NATIVE_PACKAGE_ROOT_FOLDER, '..');
198
+ const configDir = baseCodegenConfigFileDir || process.cwd();
195
199
  console.log(
196
200
  `\n\n[Codegen] >>>>> Searching for codegen-enabled libraries in ${configDir}`,
197
201
  );
198
202
 
199
203
  // Handle third-party libraries
204
+ const resolveOptions = {paths: [configDir]};
200
205
  Object.keys(dependencies).forEach(dependency => {
201
206
  if (dependency === REACT_NATIVE_DEPENDENCY_NAME) {
202
207
  // react-native should already be added.
203
208
  return;
204
209
  }
205
- const codegenConfigFileDir = path.join(configDir, dependency);
206
- const configFilePath = path.join(
207
- codegenConfigFileDir,
208
- codegenConfigFilename,
209
- );
210
- if (fs.existsSync(configFilePath)) {
210
+
211
+ try {
212
+ const configFilePath = require.resolve(
213
+ `${dependency}/${codegenConfigFilename}`,
214
+ resolveOptions,
215
+ );
211
216
  const configFile = JSON.parse(fs.readFileSync(configFilePath));
212
217
  extractLibrariesFromJSON(
213
218
  configFile,
214
219
  libraries,
215
220
  codegenConfigKey,
216
221
  dependency,
217
- codegenConfigFileDir,
222
+ path.dirname(configFilePath),
218
223
  );
224
+ } catch (_) {
225
+ // ignore
219
226
  }
220
227
  });
221
228
  }
@@ -313,6 +313,7 @@ def react_native_post_install(
313
313
  ReactNativePodsUtils.apply_flags_for_fabric(installer, fabric_enabled: fabric_enabled)
314
314
  ReactNativePodsUtils.apply_xcode_15_patch(installer)
315
315
  ReactNativePodsUtils.updateOSDeploymentTarget(installer)
316
+ ReactNativePodsUtils.fix_flipper_for_xcode_15_3(installer)
316
317
 
317
318
  NewArchitectureHelper.set_clang_cxx_language_standard_if_needed(installer)
318
319
  NewArchitectureHelper.modify_flags_for_new_architecture(installer, NewArchitectureHelper.new_arch_enabled)
@@ -13,7 +13,7 @@
13
13
  # ./with-environment.sh command
14
14
 
15
15
  # Start with a default
16
- NODE_BINARY=$(command -v node)
16
+ NODE_BINARY=$(command -v node || echo "")
17
17
  export NODE_BINARY
18
18
 
19
19
  # Override the default with the global environment
@@ -1 +1 @@
1
- hermes-2024-01-31-RNv0.73.3-398783c198253f61e0a5eb603f1eb7b55af6baa4
1
+ hermes-2024-03-01-RNv0.73.5-5e3ba8a5ff73efc46f4570492176b0605de77ed6
@@ -7,6 +7,7 @@ require 'net/http'
7
7
  require 'rexml/document'
8
8
 
9
9
  HERMES_GITHUB_URL = "https://github.com/facebook/hermes.git"
10
+ ENV_BUILD_FROM_SOURCE = "RCT_BUILD_HERMES_FROM_SOURCE"
10
11
 
11
12
  module HermesEngineSourceType
12
13
  LOCAL_PREBUILT_TARBALL = :local_prebuilt_tarball
@@ -30,7 +31,7 @@ end
30
31
  # - To use a specific tarball, install the dependencies with:
31
32
  # `HERMES_ENGINE_TARBALL_PATH=<path_to_tarball> bundle exec pod install`
32
33
  # - To force a build from source, install the dependencies with:
33
- # `BUILD_FROM_SOURCE=true bundle exec pod install`
34
+ # `RCT_BUILD_HERMES_FROM_SOURCE=true bundle exec pod install`
34
35
  # If none of the two are provided, Cocoapods will check whether there is a tarball for the current version
35
36
  # (either release or nightly). If not, it will fall back to building from source (the latest commit on main).
36
37
  #
@@ -85,11 +86,11 @@ def hermes_commit_envvar_defined()
85
86
  end
86
87
 
87
88
  def force_build_from_tag(react_native_path)
88
- return ENV['BUILD_FROM_SOURCE'] === 'true' && File.exist?(hermestag_file(react_native_path))
89
+ return ENV[ENV_BUILD_FROM_SOURCE] === 'true' && File.exist?(hermestag_file(react_native_path))
89
90
  end
90
91
 
91
92
  def force_build_from_main(react_native_path)
92
- return ENV['BUILD_FROM_SOURCE'] === 'true' && !File.exist?(hermestag_file(react_native_path))
93
+ return ENV[ENV_BUILD_FROM_SOURCE] === 'true' && !File.exist?(hermestag_file(react_native_path))
93
94
  end
94
95
 
95
96
  def release_artifact_exists(version)
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "expo": "^50.0.2",
15
15
  "react": "18.2.0",
16
- "react-native": "npm:react-native-tvos@0.73.4-0rc0"
16
+ "react-native": "npm:react-native-tvos@0.73.6-0"
17
17
  },
18
18
  "devDependencies": {
19
19
  "@babel/core": "^7.20.0",