diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index 00851d6c9e2fe9..d45a858b628834 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -129,7 +129,11 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps]; } #if !TARGET_OS_OSX // [macOS] +#if !TARGET_OS_VISION // [visionOS] self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; +#else + self.window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 1280, 720)]; +#endif // [visionOS] UIViewController *rootViewController = [self createRootViewController]; [self setRootView:rootView toRootViewController:rootViewController]; self.window.rootViewController = rootViewController; @@ -138,7 +142,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification return YES; #else // [macOS - NSRect frame = NSMakeRect(0,0,1024,768); + NSRect frame = NSMakeRect(0,0,1280,720); self.window = [[NSWindow alloc] initWithContentRect:NSZeroRect styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable backing:NSBackingStoreBuffered diff --git a/packages/react-native/Libraries/Image/RCTUIImageViewAnimated.mm b/packages/react-native/Libraries/Image/RCTUIImageViewAnimated.mm index ce209e06a4487e..da7751226363f3 100644 --- a/packages/react-native/Libraries/Image/RCTUIImageViewAnimated.mm +++ b/packages/react-native/Libraries/Image/RCTUIImageViewAnimated.mm @@ -193,15 +193,10 @@ - (BOOL)paused - (void)displayDidRefresh:(CADisplayLink *)displayLink { -#if TARGET_OS_UIKITFORMAC - // TODO: `displayLink.frameInterval` is not available on UIKitForMac - NSTimeInterval durationToNextRefresh = displayLink.duration; -#else // displaylink.duration -- time interval between frames, assuming maximumFramesPerSecond // displayLink.preferredFramesPerSecond (>= iOS 10) -- Set to 30 for displayDidRefresh to be called at 30 fps // durationToNextRefresh -- Time interval to the next time displayDidRefresh is called NSTimeInterval durationToNextRefresh = displayLink.targetTimestamp - displayLink.timestamp; -#endif NSUInteger totalFrameCount = self.totalFrameCount; NSUInteger currentFrameIndex = self.currentFrameIndex; NSUInteger nextFrameIndex = (currentFrameIndex + 1) % totalFrameCount; diff --git a/packages/react-native/Libraries/Network/React-RCTNetwork.podspec b/packages/react-native/Libraries/Network/React-RCTNetwork.podspec index 1e023540e0191f..2f1ba27a0e3ad9 100644 --- a/packages/react-native/Libraries/Network/React-RCTNetwork.podspec +++ b/packages/react-native/Libraries/Network/React-RCTNetwork.podspec @@ -42,7 +42,10 @@ Pod::Spec.new do |s| "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", "HEADER_SEARCH_PATHS" => header_search_paths.join(' ') } - s.ios.frameworks = "MobileCoreServices" # [macOS] Restrict to iOS + # [macOS MobileCoreServices Not available on macOS + s.ios.frameworks = "MobileCoreServices" + s.visionos.frameworks = "MobileCoreServices" + # macOS] s.dependency "RCT-Folly", folly_version s.dependency "RCTTypeSafety" diff --git a/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.h b/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.h index f6fdb8c4e7e660..b7944837bf9147 100644 --- a/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.h +++ b/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.h @@ -15,18 +15,18 @@ extern NSString *const RCTRemoteNotificationReceived; typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result); #endif // [macOS] -#if !TARGET_OS_UIKITFORMAC + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; + (void)didReceiveRemoteNotification:(NSDictionary *)notification; #if !TARGET_OS_OSX // [macOS] + (void)didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler; +#if TARGET_OS_IOS // [visionOS] + (void)didReceiveLocalNotification:(UILocalNotification *)notification; +#endif // [visionOS] #endif // [macOS] #if TARGET_OS_OSX // [macOS + (void)didReceiveUserNotification:(NSUserNotification *)notification; #endif // macOS] + (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; -#endif @end diff --git a/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm b/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm index c0c08ab746c853..3e7a6e6826d5f1 100644 --- a/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm +++ b/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm @@ -24,8 +24,6 @@ static NSString *const kErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS"; -#if !TARGET_OS_UIKITFORMAC - @interface RCTPushNotificationManager () @property (nonatomic, strong) NSMutableDictionary *remoteNotificationCallbacks; @end @@ -97,16 +95,10 @@ @implementation RCTConvert (UIBackgroundFetchResult) @end #endif // [macOS] -#else -@interface RCTPushNotificationManager () -@end -#endif // TARGET_OS_UIKITFORMAC @implementation RCTPushNotificationManager -#if !TARGET_OS_UIKITFORMAC - -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] /** DEPRECATED. UILocalNotification was deprecated in iOS 10. Please don't add new callsites. */ static NSDictionary *RCTFormatLocalNotification(UILocalNotification *notification) { @@ -126,7 +118,8 @@ @implementation RCTPushNotificationManager formattedLocalNotification[@"remote"] = @NO; return formattedLocalNotification; } -#else // [macOS +#endif // [macOS] [visionOS] +#if TARGET_OS_OSX // [macOS static NSDictionary *RCTFormatUserNotification(NSUserNotification *notification) { NSMutableDictionary *formattedUserNotification = [NSMutableDictionary dictionary]; @@ -198,8 +191,6 @@ @implementation RCTPushNotificationManager return [formatter stringFromDate:date]; } -#endif // TARGET_OS_UIKITFORMAC - RCT_EXPORT_MODULE() - (dispatch_queue_t)methodQueue @@ -207,7 +198,6 @@ - (dispatch_queue_t)methodQueue return dispatch_get_main_queue(); } -#if !TARGET_OS_UIKITFORMAC - (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self @@ -282,14 +272,15 @@ + (void)didReceiveRemoteNotification:(NSDictionary *)notification } #endif // [macOS] -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] + (void)didReceiveLocalNotification:(UILocalNotification *)notification { [[NSNotificationCenter defaultCenter] postNotificationName:kLocalNotificationReceived object:self userInfo:RCTFormatLocalNotification(notification)]; } -#else // [macOS +#endif // [macOS] [visionOS] +#if TARGET_OS_OSX // [macOS + (void)didReceiveUserNotification:(NSUserNotification *)notification { NSString *notificationName = notification.isRemote ? RCTRemoteNotificationReceived : kLocalNotificationReceived; @@ -568,7 +559,7 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification : (RCTPromiseResolveBlock)resolve reject : (__unused RCTPromiseRejectBlock)reject) { -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] NSMutableDictionary *initialNotification = [self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] mutableCopy]; @@ -583,7 +574,8 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification } else { resolve((id)kCFNull); } -#else // [macOS +#endif // [macOS] [visionOS] +#if TARGET_OS_OSX // [macOS NSUserNotification *initialNotification = self.bridge.launchOptions[NSApplicationLaunchUserNotificationKey]; if (initialNotification) { resolve(RCTFormatUserNotification(initialNotification)); @@ -638,100 +630,6 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification }]; } -#else // TARGET_OS_UIKITFORMAC - -RCT_EXPORT_METHOD(onFinishRemoteNotification : (NSString *)notificationId fetchResult : (NSString *)fetchResult) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(setApplicationIconBadgeNumber : (double)number) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(getApplicationIconBadgeNumber : (RCTResponseSenderBlock)callback) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(requestPermissions - : (JS::NativePushNotificationManagerIOS::SpecRequestPermissionsPermission &)permissions resolve - : (RCTPromiseResolveBlock)resolve reject - : (RCTPromiseRejectBlock)reject) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(abandonPermissions) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(checkPermissions : (RCTResponseSenderBlock)callback) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(presentLocalNotification : (JS::NativePushNotificationManagerIOS::Notification &)notification) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(scheduleLocalNotification : (JS::NativePushNotificationManagerIOS::Notification &)notification) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(cancelAllLocalNotifications) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(cancelLocalNotifications : (NSDictionary *)userInfo) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(getInitialNotification - : (RCTPromiseResolveBlock)resolve reject - : (__unused RCTPromiseRejectBlock)reject) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(getScheduledLocalNotifications : (RCTResponseSenderBlock)callback) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(removeAllDeliveredNotifications) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(removeDeliveredNotifications : (NSArray *)identifiers) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(getDeliveredNotifications : (RCTResponseSenderBlock)callback) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -RCT_EXPORT_METHOD(getAuthorizationStatus : (RCTResponseSenderBlock)callback) -{ - RCTLogError(@"Not implemented: %@", NSStringFromSelector(_cmd)); -} - -- (NSArray *)supportedEvents -{ - return @[]; -} - -#endif // TARGET_OS_UIKITFORMAC - - (std::shared_ptr)getTurboModule: (const facebook::react::ObjCTurboModule::InitParams &)params { diff --git a/packages/react-native/Libraries/Text/React-RCTText.podspec b/packages/react-native/Libraries/Text/React-RCTText.podspec index 800b5f726654ac..103e930b373440 100644 --- a/packages/react-native/Libraries/Text/React-RCTText.podspec +++ b/packages/react-native/Libraries/Text/React-RCTText.podspec @@ -30,7 +30,10 @@ Pod::Spec.new do |s| s.ios.exclude_files = "**/macOS/*" # [macOS] s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" s.header_dir = "RCTText" - s.ios.framework = ["MobileCoreServices"] # [macOS] Restrict to iOS + # [macOS MobileCoreServices Not available on macOS + s.ios.frameworks = "MobileCoreServices" + s.visionos.frameworks = "MobileCoreServices" + # macOS] s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => "c++20" } s.dependency "Yoga" diff --git a/packages/react-native/Libraries/Text/Text/RCTTextView.mm b/packages/react-native/Libraries/Text/Text/RCTTextView.mm index 3f47a3a23b8787..d2a7d3a646729f 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextView.mm +++ b/packages/react-native/Libraries/Text/Text/RCTTextView.mm @@ -373,29 +373,21 @@ - (void)disableContextMenu - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { -#if !TARGET_OS_UIKITFORMAC - if (@available(iOS 16.0, *)) { + if (@available(iOS 16.0, macCatalyst 16.0, *)) { CGPoint location = [gesture locationInView:self]; UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:location]; if (_editMenuInteraction) { [_editMenuInteraction presentEditMenuWithConfiguration:config]; } - return; - } - // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) - UIMenuController *menuController = [UIMenuController sharedMenuController]; + } else { + UIMenuController *menuController = [UIMenuController sharedMenuController]; - if (menuController.isMenuVisible) { - return; - } + if (menuController.isMenuVisible) { + return; + } - if (!self.isFirstResponder) { - [self becomeFirstResponder]; + [menuController showMenuFromView:self rect:self.bounds]; } - - [menuController setTargetRect:self.bounds inView:self]; - [menuController setMenuVisible:YES animated:YES]; -#endif } #else // [macOS diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm index c7b66f0cfb892d..36015db3ce7735 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm +++ b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm @@ -851,6 +851,7 @@ - (void)didMoveToWindow #pragma mark - Custom Input Accessory View +#if TARGET_OS_IOS // [macOS] [visionOS] - (void)didSetProps:(NSArray *)changedProps { if ([changedProps containsObject:@"inputAccessoryViewID"] && self.inputAccessoryViewID) { @@ -862,7 +863,6 @@ - (void)didSetProps:(NSArray *)changedProps - (void)setCustomInputAccessoryViewWithNativeID:(NSString *)nativeID { -#if !TARGET_OS_OSX // [macOS] __weak RCTBaseTextInputView *weakSelf = self; [_bridge.uiManager rootViewForReactTag:self.reactTag withCompletion:^(UIView *rootView) { @@ -877,12 +877,10 @@ - (void)setCustomInputAccessoryViewWithNativeID:(NSString *)nativeID } } }]; -#endif // [macOS] } - (void)setDefaultInputAccessoryView { -#if !TARGET_OS_OSX // [macOS] UIView *textInputView = self.backedTextInputView; UIKeyboardType keyboardType = textInputView.keyboardType; @@ -914,10 +912,8 @@ - (void)setDefaultInputAccessoryView textInputView.inputAccessoryView = nil; } [self reloadInputViewsIfNecessary]; -#endif // [macOS] } -#if !TARGET_OS_OSX // [macOS] - (void)reloadInputViewsIfNecessary { // We have to call `reloadInputViews` for focused text inputs to update an accessory view. @@ -934,7 +930,7 @@ - (void)handleInputAccessoryDoneButton [self.backedTextInputView endEditing:YES]; } } -#endif // [macOS] +#endif // [macOS] [visionOS] // [macOS diff --git a/packages/react-native/Libraries/Text/TextInput/Singleline/macOS/RCTUISecureTextField.h b/packages/react-native/Libraries/Text/TextInput/Singleline/macOS/RCTUISecureTextField.h index 792c6703c41d58..c0586c7be5dbfd 100644 --- a/packages/react-native/Libraries/Text/TextInput/Singleline/macOS/RCTUISecureTextField.h +++ b/packages/react-native/Libraries/Text/TextInput/Singleline/macOS/RCTUISecureTextField.h @@ -7,6 +7,9 @@ // [macOS] +#if TARGET_OS_OSX #define RCT_SUBCLASS_SECURETEXTFIELD 1 +#endif #include + diff --git a/packages/react-native/Libraries/Text/TextInput/Singleline/macOS/RCTUISecureTextField.m b/packages/react-native/Libraries/Text/TextInput/Singleline/macOS/RCTUISecureTextField.m index 610a8e39a32367..41d4f4c9b7b3b3 100644 --- a/packages/react-native/Libraries/Text/TextInput/Singleline/macOS/RCTUISecureTextField.m +++ b/packages/react-native/Libraries/Text/TextInput/Singleline/macOS/RCTUISecureTextField.m @@ -7,6 +7,8 @@ // [macOS] +#if TARGET_OS_OSX #define RCT_SUBCLASS_SECURETEXTFIELD 1 +#endif #include "../RCTUITextField.mm" diff --git a/packages/react-native/React/Base/RCTConvert.m b/packages/react-native/React/Base/RCTConvert.m index 06c33ecc7bd1fc..7d1b03cda28d5b 100644 --- a/packages/react-native/React/Base/RCTConvert.m +++ b/packages/react-native/React/Base/RCTConvert.m @@ -555,8 +555,10 @@ + (UIKeyboardType)UIKeyboardType:(id)json RCT_DYNAMIC (@{ @"default" : @(UIBarStyleDefault), @"black" : @(UIBarStyleBlack), +#if !TARGET_OS_VISION // [visionOS] @"blackOpaque" : @(UIBarStyleBlackOpaque), @"blackTranslucent" : @(UIBarStyleBlackTranslucent), +#endif // [visionOS] }), UIBarStyleDefault, integerValue) diff --git a/packages/react-native/React/Base/RCTKeyCommands.m b/packages/react-native/React/Base/RCTKeyCommands.m index 978e3841839544..9121cde3c88cf1 100644 --- a/packages/react-native/React/Base/RCTKeyCommands.m +++ b/packages/react-native/React/Base/RCTKeyCommands.m @@ -129,7 +129,11 @@ - (void)handleKeyUIEventSwizzle:(UIEvent *)event isKeyDown = [event _isKeyDown]; } - BOOL interactionEnabled = !RCTSharedApplication().isIgnoringInteractionEvents; +#if !TARGET_OS_VISION // [visionOS] + BOOL interactionEnabled = !RCTSharedApplication().isIgnoringInteractionEvents; + #else // [visionOS + BOOL interactionEnabled = true; + #endif // visionOS] BOOL hasFirstResponder = NO; if (isKeyDown && modifiedInput.length > 0 && interactionEnabled) { UIResponder *firstResponder = nil; diff --git a/packages/react-native/React/Base/RCTUtils.h b/packages/react-native/React/Base/RCTUtils.h index d391b64327178e..25cea7b240742c 100644 --- a/packages/react-native/React/Base/RCTUtils.h +++ b/packages/react-native/React/Base/RCTUtils.h @@ -99,6 +99,11 @@ RCT_EXTERN RCTUIApplication *__nullable RCTSharedApplication(void); // [macOS] // or view controller RCT_EXTERN RCTUIWindow *__nullable RCTKeyWindow(void); // [macOS] +#if TARGET_OS_VISION // [visionOS +// Returns UIStatusBarManager to get it's configuration info. +RCT_EXTERN UIStatusBarManager *__nullable RCTUIStatusBarManager(void); +#endif // visionOS] + #if !TARGET_OS_OSX // [macOS] // Returns the presented view controller, useful if you need // e.g. to present a modal view controller or alert over it diff --git a/packages/react-native/React/Base/RCTUtils.m b/packages/react-native/React/Base/RCTUtils.m index 648bceacfd841b..36916fc85429f4 100644 --- a/packages/react-native/React/Base/RCTUtils.m +++ b/packages/react-native/React/Base/RCTUtils.m @@ -352,7 +352,11 @@ CGSize RCTScreenSize(void) static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ RCTUnsafeExecuteOnMainQueueSync(^{ +#if TARGET_OS_IOS // [visionOS] size = [UIScreen mainScreen].bounds.size; +#else // [visionOS + size = RCTKeyWindow().bounds.size; +#endif // visionOS] }); }); @@ -620,6 +624,14 @@ BOOL RCTRunningInAppExtension(void) #endif // macOS] } +#if TARGET_OS_VISION // [visionOS +UIStatusBarManager *__nullable RCTUIStatusBarManager(void) { + NSSet *connectedScenes = RCTSharedApplication().connectedScenes; + UIWindowScene *windowScene = [connectedScenes anyObject]; + return windowScene.statusBarManager; +} +#endif // visionOS] + #if !TARGET_OS_OSX // [macOS] UIViewController *__nullable RCTPresentedViewController(void) { diff --git a/packages/react-native/React/CoreModules/RCTDeviceInfo.mm b/packages/react-native/React/CoreModules/RCTDeviceInfo.mm index 50b75c6c7862c6..4fffe60e519014 100644 --- a/packages/react-native/React/CoreModules/RCTDeviceInfo.mm +++ b/packages/react-native/React/CoreModules/RCTDeviceInfo.mm @@ -56,27 +56,30 @@ - (void)initialize name:RCTAccessibilityManagerDidUpdateMultiplierNotification object:[_moduleRegistry moduleForName:"AccessibilityManager"]]; +#if TARGET_OS_IOS // [visionOS] _currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interfaceOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +#endif // [visionOS] #endif // [macOS] _currentInterfaceDimensions = [self _exportedDimensions]; -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interfaceOrientationDidChange) name:UIApplicationDidBecomeActiveNotification object:nil]; +#endif // [macOS] [visionOS] [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interfaceFrameDidChange) name:RCTUserInterfaceStyleDidChangeNotification object:nil]; -#endif // [macOS] + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(interfaceFrameDidChange) name:RCTWindowFrameDidChangeNotification @@ -90,8 +93,8 @@ - (void)invalidate static BOOL RCTIsIPhoneNotched() { -#if !TARGET_OS_OSX // [macOS] static BOOL isIPhoneNotched = NO; +#if TARGET_OS_IOS // [macOS] [visionOS] static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -100,11 +103,9 @@ static BOOL RCTIsIPhoneNotched() // 20pt is the top safeArea value in non-notched devices isIPhoneNotched = RCTSharedApplication().keyWindow.safeAreaInsets.top > 20; }); +#endif // [macOS] [visionOS] return isIPhoneNotched; -#else // [macOS - return NO; -#endif // macOS] } @@ -182,8 +183,7 @@ - (void)didReceiveNewContentSizeMultiplier }); } -#if !TARGET_OS_OSX // [macOS] - +#if TARGET_OS_IOS // [macOS] [visionOS] - (void)interfaceOrientationDidChange { __weak __typeof(self) weakSelf = self; @@ -224,7 +224,7 @@ - (void)_interfaceOrientationDidChange #pragma clang diagnostic pop } } -#endif // [macOS] +#endif // [macOS] [visionOS] - (void)interfaceFrameDidChange { diff --git a/packages/react-native/React/CoreModules/RCTPerfMonitor.mm b/packages/react-native/React/CoreModules/RCTPerfMonitor.mm index f728963f1a905a..816671c591136e 100644 --- a/packages/react-native/React/CoreModules/RCTPerfMonitor.mm +++ b/packages/react-native/React/CoreModules/RCTPerfMonitor.mm @@ -183,7 +183,11 @@ - (UIPanGestureRecognizer *)gestureRecognizer - (UIView *)container { if (!_container) { - CGSize statusBarSize = RCTSharedApplication().statusBarFrame.size; +#if !TARGET_OS_VISION // [visionOS] + CGSize statusBarSize = RCTSharedApplication().statusBarFrame.size; +#else // [visionOS + CGSize statusBarSize = RCTUIStatusBarManager().statusBarFrame.size; +#endif // visionOS] CGFloat statusBarHeight = statusBarSize.height; _container = [[UIView alloc] initWithFrame:CGRectMake(10, statusBarHeight, 180, RCTPerfMonitorBarHeight)]; _container.layer.borderWidth = 2; diff --git a/packages/react-native/React/CoreModules/RCTRedBox.mm b/packages/react-native/React/CoreModules/RCTRedBox.mm index 33113b5b8a09ca..736494693ebc21 100644 --- a/packages/react-native/React/CoreModules/RCTRedBox.mm +++ b/packages/react-native/React/CoreModules/RCTRedBox.mm @@ -953,9 +953,15 @@ - (void)showErrorMessage:(NSString *)message if (!self->_window) { #if !TARGET_OS_OSX // [macOS] +#if !TARGET_OS_VISION // [macOS] self->_window = [[RCTRedBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds customButtonTitles:self->_customButtonTitles customButtonHandlers:self->_customButtonHandlers]; +#else // [visionOS + self->_window = [[RCTRedBoxWindow alloc] initWithFrame:CGRectMake(0, 0, 1280, 720) + customButtonTitles:self->_customButtonTitles + customButtonHandlers:self->_customButtonHandlers]; +#endif // visionOS] #else // [macOS self->_window = [RCTRedBoxWindow new]; #endif // macOS] diff --git a/packages/react-native/React/CoreModules/RCTStatusBarManager.mm b/packages/react-native/React/CoreModules/RCTStatusBarManager.mm index b56b3ae6a2ecc1..239d8424c62e70 100644 --- a/packages/react-native/React/CoreModules/RCTStatusBarManager.mm +++ b/packages/react-native/React/CoreModules/RCTStatusBarManager.mm @@ -12,9 +12,9 @@ #import #import -#if !TARGET_OS_OSX // [macOS] #import +#if !TARGET_OS_OSX // [macOS] @implementation RCTConvert (UIStatusBar) + (UIStatusBarStyle)UIStatusBarStyle:(id)json RCT_DYNAMIC @@ -44,14 +44,14 @@ + (UIStatusBarStyle)UIStatusBarStyle:(id)json RCT_DYNAMIC integerValue); @end +#endif // [macOS] @interface RCTStatusBarManager () @end -#endif // [macOS] - @implementation RCTStatusBarManager +#if !TARGET_OS_OSX // [macOS] static BOOL RCTViewControllerBasedStatusBarAppearance() { static BOOL value; @@ -64,6 +64,7 @@ static BOOL RCTViewControllerBasedStatusBarAppearance() return value; } +#endif // [macOS] RCT_EXPORT_MODULE() @@ -72,15 +73,15 @@ + (BOOL)requiresMainQueueSetup return YES; } +#if TARGET_OS_OSX // [macOS] - (NSArray *)supportedEvents { return @[ @"statusBarFrameDidChange", @"statusBarFrameWillChange" ]; } -#if !TARGET_OS_OSX // [macOS] - - (void)startObserving { +#if TARGET_OS_IOS // [visionOS] NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(applicationDidChangeStatusBarFrame:) @@ -90,6 +91,7 @@ - (void)startObserving selector:@selector(applicationWillChangeStatusBarFrame:) name:UIApplicationWillChangeStatusBarFrameNotification object:nil]; +#endif // [visionOS] } - (void)stopObserving @@ -104,6 +106,7 @@ - (dispatch_queue_t)methodQueue - (void)emitEvent:(NSString *)eventName forNotification:(NSNotification *)notification { +#if TARGET_OS_IOS // [visionOS] CGRect frame = [notification.userInfo[UIApplicationStatusBarFrameUserInfoKey] CGRectValue]; NSDictionary *event = @{ @"frame" : @{ @@ -114,6 +117,7 @@ - (void)emitEvent:(NSString *)eventName forNotification:(NSNotification *)notifi }, }; [self sendEventWithName:eventName body:event]; +#endif // [visionOS] } - (void)applicationDidChangeStatusBarFrame:(NSNotification *)notification @@ -125,19 +129,34 @@ - (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification { [self emitEvent:@"statusBarFrameWillChange" forNotification:notification]; } +#endif RCT_EXPORT_METHOD(getHeight : (RCTResponseSenderBlock)callback) { +#if !TARGET_OS_OSX // [macOS] +#if !TARGET_OS_VISION // [visionOS] callback(@[ @{ @"height" : @(RCTSharedApplication().statusBarFrame.size.height), } ]); +#else // [visionOS + callback(@[ @{ + @"height" : @(RCTUIStatusBarManager().statusBarFrame.size), + } ]); +#endif // visionOS] +#else // [macOS + callback(@[ @{ + @"height" : @(0), + } ]); +#endif // macOS] } RCT_EXPORT_METHOD(setStyle : (NSString *)style animated : (BOOL)animated) { - UIStatusBarStyle statusBarStyle = [RCTConvert UIStatusBarStyle:style]; - if (RCTViewControllerBasedStatusBarAppearance()) { - RCTLogError(@"RCTStatusBarManager module requires that the \ +#if TARGET_OS_IOS // [macOS] [visionOS] + dispatch_async(dispatch_get_main_queue(), ^{ + UIStatusBarStyle statusBarStyle = [RCTConvert UIStatusBarStyle:style]; + if (RCTViewControllerBasedStatusBarAppearance()) { + RCTLogError(@"RCTStatusBarManager module requires that the \ UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO"); } else { #pragma clang diagnostic push @@ -145,25 +164,35 @@ - (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification [RCTSharedApplication() setStatusBarStyle:statusBarStyle animated:animated]; } #pragma clang diagnostic pop + }); +#endif // [macOS] [visionOS] } RCT_EXPORT_METHOD(setHidden : (BOOL)hidden withAnimation : (NSString *)withAnimation) { - UIStatusBarAnimation animation = [RCTConvert UIStatusBarAnimation:withAnimation]; - if (RCTViewControllerBasedStatusBarAppearance()) { - RCTLogError(@"RCTStatusBarManager module requires that the \ +#if TARGET_OS_IOS // [macOS] [visionOS] + dispatch_async(dispatch_get_main_queue(), ^{ + UIStatusBarAnimation animation = [RCTConvert UIStatusBarAnimation:withAnimation]; + if (RCTViewControllerBasedStatusBarAppearance()) { + RCTLogError(@"RCTStatusBarManager module requires that the \ UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO"); } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" [RCTSharedApplication() setStatusBarHidden:hidden withAnimation:animation]; #pragma clang diagnostic pop - } + } + }); +#endif // [macOS] [visionOS] } RCT_EXPORT_METHOD(setNetworkActivityIndicatorVisible : (BOOL)visible) { - RCTSharedApplication().networkActivityIndicatorVisible = visible; +#if TARGET_OS_IOS // [visionOS] + dispatch_async(dispatch_get_main_queue(), ^{ + RCTSharedApplication().networkActivityIndicatorVisible = visible; + }); +#endif // [visionOS] } - (facebook::react::ModuleConstants)getConstants @@ -171,7 +200,15 @@ - (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification __block facebook::react::ModuleConstants constants; RCTUnsafeExecuteOnMainQueueSync(^{ constants = facebook::react::typedConstants({ +#if !TARGET_OS_OSX // [macOS] +#if !TARGET_OS_VISION // [visionOS] .HEIGHT = RCTSharedApplication().statusBarFrame.size.height, +#else // [visionOS + .HEIGHT = RCTUIStatusBarManager().statusBarFrame.size.height, +#endif // visionOS] +#else // [macOS + .HEIGHT = 0, +#endif // macOS] .DEFAULT_BACKGROUND_COLOR = std::nullopt, }); }); @@ -190,8 +227,6 @@ - (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification return std::make_shared(params); } -#endif // [macOS] - @end Class RCTStatusBarManagerCls(void) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/InputAccessory/RCTInputAccessoryComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/InputAccessory/RCTInputAccessoryComponentView.mm index e9dabd8f5bc4fa..04e336d8667889 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/InputAccessory/RCTInputAccessoryComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/InputAccessory/RCTInputAccessoryComponentView.mm @@ -68,7 +68,9 @@ - (void)didMoveToWindow if (self.nativeId) { #if !TARGET_OS_OSX // [macOS] _textInput = RCTFindTextInputWithNativeId(self.window, self.nativeId); +#if !TARGET_OS_VISION // [visionOS] _textInput.inputAccessoryView = _contentView; +#endif // [visionOS] #else // [macOS _textInput = RCTFindTextInputWithNativeId(self.window.contentView, self.nativeId); #endif // macOS] @@ -87,10 +89,12 @@ - (BOOL)canBecomeFirstResponder return true; } +#if !TARGET_OS_VISION // [visionOS] - (RCTUIView *)inputAccessoryView // [macOS] { return _contentView; } +#endif // [visionOS] #pragma mark - RCTComponentViewProtocol diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm index b7d2b3c5a53614..29e45d4d6f0f80 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Modal/RCTFabricModalHostViewController.mm @@ -43,10 +43,12 @@ - (void)loadView [_touchHandler attachToView:self.view]; } +#if TARGET_OS_IOS // [visionOS] - (UIStatusBarStyle)preferredStatusBarStyle { return [RCTSharedApplication() statusBarStyle]; } +#endif // [visionOS] - (void)viewDidDisappear:(BOOL)animated { @@ -54,16 +56,22 @@ - (void)viewDidDisappear:(BOOL)animated _lastViewBounds = CGRectZero; } +#if TARGET_OS_IOS // [visionOS] - (BOOL)prefersStatusBarHidden { return [RCTSharedApplication() isStatusBarHidden]; } +#endif // [visionOS] #if RCT_DEV - (UIInterfaceOrientationMask)supportedInterfaceOrientations { +#if !TARGET_OS_VISION // [visionOS] UIInterfaceOrientationMask appSupportedOrientationsMask = [RCTSharedApplication() supportedInterfaceOrientationsForWindow:[RCTSharedApplication() keyWindow]]; +#else // [visionOS + UIInterfaceOrientationMask appSupportedOrientationsMask = UIInterfaceOrientationMaskLandscape; +#endif // visonOS] if (!(_supportedInterfaceOrientations & appSupportedOrientationsMask)) { RCTLogError( @"Modal was presented with 0x%x orientations mask but the application only supports 0x%x." diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index a5c85a84c48d6b..81a0b79d6fd98e 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -28,7 +28,7 @@ static const CGFloat kClippingLeeway = 44.0; -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] static UIScrollViewKeyboardDismissMode RCTUIKeyboardDismissModeFromProps(const ScrollViewProps &props) { switch (props.keyboardDismissMode) { @@ -40,7 +40,9 @@ static UIScrollViewKeyboardDismissMode RCTUIKeyboardDismissModeFromProps(const S return UIScrollViewKeyboardDismissModeInteractive; } } +#endif // [macOS] [visionOS] +#if !TARGET_OS_OSX // [macOS static UIScrollViewIndicatorStyle RCTUIScrollViewIndicatorStyleFromProps(const ScrollViewProps &props) { switch (props.indicatorStyle) { @@ -52,6 +54,7 @@ static UIScrollViewIndicatorStyle RCTUIScrollViewIndicatorStyleFromProps(const S return UIScrollViewIndicatorStyleWhite; } } +#endif // [macOS] // Once Fabric implements proper NativeAnimationDriver, this should be removed. // This is just a workaround to allow animations based on onScroll event. @@ -79,7 +82,6 @@ static void RCTSendScrollEventForNativeAnimations_DEPRECATED(RCTUIScrollView *sc userInfo:userInfo]; } } -#endif // [macOS] @interface RCTScrollViewComponentView () < #if !TARGET_OS_OSX // [macOS] @@ -329,9 +331,9 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & MAP_SCROLL_VIEW_PROP(snapToInterval); if (oldScrollViewProps.keyboardDismissMode != newScrollViewProps.keyboardDismissMode) { -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] scrollView.keyboardDismissMode = RCTUIKeyboardDismissModeFromProps(newScrollViewProps); -#endif // [macOS] +#endif // [macOS] [visionOS] } [super updateProps:props oldProps:oldProps]; diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm index 8a20e5326707b0..74026ca2292976 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -251,29 +251,21 @@ - (void)disableContextMenu - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { -#if !TARGET_OS_UIKITFORMAC - if (@available(iOS 16.0, *)) { + if (@available(iOS 16.0, macCatalyst 16.0, *)) { CGPoint location = [gesture locationInView:self]; UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:location]; if (_editMenuInteraction) { [_editMenuInteraction presentEditMenuWithConfiguration:config]; } - return; - } - // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) - UIMenuController *menuController = [UIMenuController sharedMenuController]; + } else { + UIMenuController *menuController = [UIMenuController sharedMenuController]; - if (menuController.isMenuVisible) { - return; - } + if (menuController.isMenuVisible) { + return; + } - if (!self.isFirstResponder) { - [self becomeFirstResponder]; + [menuController showMenuFromView:self rect:self.bounds]; } - - [menuController setTargetRect:self.bounds inView:self]; - [menuController setMenuVisible:YES animated:YES]; -#endif } #endif // [macOS] diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index fda7f88c6ebd5d..2c12cfa47909ab 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -563,6 +563,7 @@ - (void)setDefaultInputAccessoryView keyboardType == UIKeyboardTypeDecimalPad || keyboardType == UIKeyboardTypeASCIICapableNumberPad) && _backedTextInputView.returnKeyType == UIReturnKeyDone; +#if !TARGET_OS_VISION // [visionOS] if ((_backedTextInputView.inputAccessoryView != nil) == shouldHaveInputAccessoryView) { return; } @@ -581,7 +582,8 @@ - (void)setDefaultInputAccessoryView } else { _backedTextInputView.inputAccessoryView = nil; } - +#endif // [visionOS] + if (_backedTextInputView.isFirstResponder) { [_backedTextInputView reloadInputViews]; } diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm index bac8eeb7685704..319ec09fedcfa5 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm @@ -32,9 +32,9 @@ void RCTCopyBackedTextInput( toTextInput.placeholder = fromTextInput.placeholder; toTextInput.placeholderColor = fromTextInput.placeholderColor; toTextInput.textContainerInset = fromTextInput.textContainerInset; -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] toTextInput.inputAccessoryView = fromTextInput.inputAccessoryView; -#endif // [macOS] +#endif // [macOS] [visionOS] toTextInput.textInputDelegate = fromTextInput.textInputDelegate; toTextInput.placeholderColor = fromTextInput.placeholderColor; toTextInput.defaultTextAttributes = fromTextInput.defaultTextAttributes; diff --git a/packages/react-native/React/Fabric/RCTSurfacePointerHandler.mm b/packages/react-native/React/Fabric/RCTSurfacePointerHandler.mm index c54c46e3a88573..559d528d43311c 100644 --- a/packages/react-native/React/Fabric/RCTSurfacePointerHandler.mm +++ b/packages/react-native/React/Fabric/RCTSurfacePointerHandler.mm @@ -291,9 +291,11 @@ static PointerEvent CreatePointerEventFromActivePointer( if (eventType == RCTPointerEventTypeCancel) { event.clientPoint = RCTPointFromCGPoint(CGPointZero); +#if !TARGET_OS_VISION // [visionOS] event.screenPoint = RCTPointFromCGPoint([rootComponentView convertPoint:CGPointZero toCoordinateSpace:rootComponentView.window.screen.coordinateSpace]); +#endif // [visionOS] event.offsetPoint = RCTPointFromCGPoint([rootComponentView convertPoint:CGPointZero toView:activePointer.componentView]); } else { @@ -386,8 +388,10 @@ static void UpdateActivePointerWithUITouch( #if !TARGET_OS_OSX // [macOS] activePointer.clientPoint = [uiTouch locationInView:rootComponentView]; +#if !TARGET_OS_VISION // [visionOS] activePointer.screenPoint = [rootComponentView convertPoint:activePointer.clientPoint toCoordinateSpace:rootComponentView.window.screen.coordinateSpace]; +#endif // [visionOS] activePointer.offsetPoint = [uiTouch locationInView:activePointer.componentView]; #else // [macOS activePointer.offsetPoint = [activePointer.componentView convertPoint:uiTouch.locationInWindow fromView:nil]; @@ -910,9 +914,12 @@ - (void)hovering:(UIHoverGestureRecognizer *)recognizer { UIView *listenerView = recognizer.view; CGPoint clientLocation = [recognizer locationInView:listenerView]; +#if !TARGET_OS_VISION // [visionOS] CGPoint screenLocation = [listenerView convertPoint:clientLocation toCoordinateSpace:listenerView.window.screen.coordinateSpace]; - +#else // [visionOS + CGPoint screenLocation = CGPointZero; +#endif // visionOS] UIView *targetView = [listenerView hitTest:clientLocation withEvent:nil]; targetView = FindClosestFabricManagedTouchableView(targetView); diff --git a/packages/react-native/React/Fabric/RCTSurfaceTouchHandler.mm b/packages/react-native/React/Fabric/RCTSurfaceTouchHandler.mm index c58cd5f237646b..fc83e173dc51ae 100644 --- a/packages/react-native/React/Fabric/RCTSurfaceTouchHandler.mm +++ b/packages/react-native/React/Fabric/RCTSurfaceTouchHandler.mm @@ -58,8 +58,12 @@ static void UpdateActiveTouchWithUITouch( #if !TARGET_OS_OSX // [macOS] CGPoint offsetPoint = [uiTouch locationInView:activeTouch.componentView]; CGPoint pagePoint = [uiTouch locationInView:rootComponentView]; +#if !TARGET_OS_VISION // [visionOS] CGPoint screenPoint = [rootComponentView convertPoint:pagePoint toCoordinateSpace:rootComponentView.window.screen.coordinateSpace]; +#else // [visionOS + CGPoint screenPoint = CGPointZero; +#endif // visionOS] pagePoint = CGPointMake(pagePoint.x + rootViewOriginOffset.x, pagePoint.y + rootViewOriginOffset.y); #else // [macOS CGPoint offsetPoint = [activeTouch.componentView convertPoint:uiTouch.locationInWindow fromView:nil]; diff --git a/packages/react-native/React/Modules/RCTUIManager.m b/packages/react-native/React/Modules/RCTUIManager.m index 2fbb57c8806ec6..4db130c08846c4 100644 --- a/packages/react-native/React/Modules/RCTUIManager.m +++ b/packages/react-native/React/Modules/RCTUIManager.m @@ -198,10 +198,12 @@ - (void)setBridge:(RCTBridge *)bridge object:[self->_bridge moduleForName:@"AccessibilityManager" lazilyLoadIfNecessary:YES]]; }); +#if TARGET_OS_IOS // [visionOS] [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(namedOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; +#endif // [visionOS] [RCTLayoutAnimation initializeStatics]; #endif // [macOS] } @@ -231,7 +233,7 @@ - (void)didReceiveNewContentSizeMultiplier } #endif // [macOS] -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] // Names and coordinate system from html5 spec: // https://developer.mozilla.org/en-US/docs/Web/API/Screen.orientation // https://developer.mozilla.org/en-US/docs/Web/API/Screen.lockOrientation @@ -284,7 +286,7 @@ - (void)namedOrientationDidChange body:orientationEvent]; #pragma clang diagnostic pop } -#endif // macOS] +#endif // [macOS] [visionOS] - (dispatch_queue_t)methodQueue { diff --git a/packages/react-native/React/React-RCTFabric.podspec b/packages/react-native/React/React-RCTFabric.podspec index d0cbebfe50e0fd..e9b19e35a0ae28 100644 --- a/packages/react-native/React/React-RCTFabric.podspec +++ b/packages/react-native/React/React-RCTFabric.podspec @@ -55,8 +55,11 @@ Pod::Spec.new do |s| s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags s.header_dir = header_dir s.module_name = module_name - s.ios.framework = ["JavaScriptCore", "MobileCoreServices"] # [macOS] Restrict MobileCoreServices to iOS - s.osx.framework = ["JavaScriptCore"] # [macOS] Restrict MobileCoreServices to iOS + # [macOS MobileCoreServices not available on macOS + s.ios.framework = ["JavaScriptCore", "MobileCoreServices"] + s.visionos.framework = ["JavaScriptCore", "MobileCoreServices"] + s.osx.framework = ["JavaScriptCore"] + # macOS] s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => header_search_paths, "OTHER_CFLAGS" => "$(inherited) -DRN_FABRIC_ENABLED" + " " + folly_flags, diff --git a/packages/react-native/React/UIUtils/RCTUIUtils.m b/packages/react-native/React/UIUtils/RCTUIUtils.m index e2a9bf0c426a51..c9fbbc22222bab 100644 --- a/packages/react-native/React/UIUtils/RCTUIUtils.m +++ b/packages/react-native/React/UIUtils/RCTUIUtils.m @@ -12,24 +12,41 @@ RCTDimensions RCTGetDimensions(CGFloat fontScale) { #if !TARGET_OS_OSX // [macOS] +#if !TARGET_OS_VISION // [visionOS] UIScreen *mainScreen = UIScreen.mainScreen; CGSize screenSize = mainScreen.bounds.size; +#else + CGSize screenSize = CGSizeZero; +#endif +#else // [macOS + NSScreen *mainScreen = NSScreen.mainScreen; + CGSize screenSize = mainScreen.frame.size; +#endif // macOS] +#if !TARGET_OS_OSX // [macOS] UIView *mainWindow = RCTKeyWindow(); // We fallback to screen size if a key window is not found. CGSize windowSize = mainWindow ? mainWindow.bounds.size : screenSize; #else // [macOS - RCTUIWindow *window = RCTKeyWindow(); - NSSize windowSize = window ? [window frame].size : CGSizeMake(0, 0); - NSSize screenSize = window ? [[window screen] frame].size : CGSizeMake(0, 0); + NSWindow *window = RCTKeyWindow(); + NSSize windowSize = window ? [window frame].size : CGSizeZero; + screenSize = window ? [[window screen] frame].size : screenSize; CGFloat scale = window ? [[window screen] backingScaleFactor] : 1.0; // Default scale to 1.0 if window is nil -#endif // macOS +#endif // macOS] + RCTDimensions result; #if !TARGET_OS_OSX // [macOS] +#if !TARGET_OS_VISION // [visionOS] typeof(result.screen) dimsScreen = { .width = screenSize.width, .height = screenSize.height, .scale = mainScreen.scale, .fontScale = fontScale}; typeof(result.window) dimsWindow = { .width = windowSize.width, .height = windowSize.height, .scale = mainScreen.scale, .fontScale = fontScale}; +#else // [visionOS hardcode the scale to a dummy value of 2 + typeof(result.screen) dimsScreen = { + .width = screenSize.width, .height = screenSize.height, .scale = 2, .fontScale = fontScale}; + typeof(result.window) dimsWindow = { + .width = windowSize.width, .height = windowSize.height, .scale = 2, .fontScale = fontScale}; +#endif // visionOS] #else // [macOS typeof(result.screen) dimsScreen = { .width = screenSize.width, diff --git a/packages/react-native/React/Views/RCTModalHostView.m b/packages/react-native/React/Views/RCTModalHostView.m index 639889f6638536..9d6e331734c446 100644 --- a/packages/react-native/React/Views/RCTModalHostView.m +++ b/packages/react-native/React/Views/RCTModalHostView.m @@ -73,6 +73,7 @@ - (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)co - (void)notifyForOrientationChange { +#if TARGET_OS_IOS // [visionOS] if (!_onOrientationChange) { return; } @@ -89,6 +90,7 @@ - (void)notifyForOrientationChange @"orientation" : isPortrait ? @"portrait" : @"landscape", }; _onOrientationChange(eventPayload); +#endif // [visionOS] } - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex diff --git a/packages/react-native/React/Views/RCTModalHostViewController.m b/packages/react-native/React/Views/RCTModalHostViewController.m index e030d8f0ae235e..36ce42fa6ad9e3 100644 --- a/packages/react-native/React/Views/RCTModalHostViewController.m +++ b/packages/react-native/React/Views/RCTModalHostViewController.m @@ -25,8 +25,10 @@ - (instancetype)init self.modalInPresentation = YES; +#if TARGET_OS_IOS // [visionOS] _preferredStatusBarStyle = [RCTSharedApplication() statusBarStyle]; _preferredStatusBarHidden = [RCTSharedApplication() isStatusBarHidden]; +#endif // [visionOS] return self; } @@ -41,6 +43,7 @@ - (void)viewDidLayoutSubviews } } +#if TARGET_OS_IOS // [visionOS] - (UIStatusBarStyle)preferredStatusBarStyle { return _preferredStatusBarStyle; @@ -50,12 +53,17 @@ - (BOOL)prefersStatusBarHidden { return _preferredStatusBarHidden; } +#endif // [visionOS] #if RCT_DEV - (UIInterfaceOrientationMask)supportedInterfaceOrientations { +#if !TARGET_OS_VISION // [visionOS] UIInterfaceOrientationMask appSupportedOrientationsMask = [RCTSharedApplication() supportedInterfaceOrientationsForWindow:[RCTSharedApplication() keyWindow]]; +#else // [visionOS + UIInterfaceOrientationMask appSupportedOrientationsMask = UIInterfaceOrientationMaskAll; +#endif // visionOS] if (!(_supportedInterfaceOrientations & appSupportedOrientationsMask)) { RCTLogError( @"Modal was presented with 0x%x orientations mask but the application only supports 0x%x." diff --git a/packages/react-native/React/Views/ScrollView/RCTScrollView.m b/packages/react-native/React/Views/ScrollView/RCTScrollView.m index ffe7919c141626..a4f13e906b2e09 100644 --- a/packages/react-native/React/Views/ScrollView/RCTScrollView.m +++ b/packages/react-native/React/Views/ScrollView/RCTScrollView.m @@ -1403,7 +1403,9 @@ -(type)getter \ RCT_SET_AND_PRESERVE_OFFSET(setDecelerationRate, decelerationRate, CGFloat) RCT_SET_AND_PRESERVE_OFFSET(setDirectionalLockEnabled, isDirectionalLockEnabled, BOOL) RCT_SET_AND_PRESERVE_OFFSET(setIndicatorStyle, indicatorStyle, UIScrollViewIndicatorStyle) +#if TARGET_OS_IOS // [visionOS] RCT_SET_AND_PRESERVE_OFFSET(setKeyboardDismissMode, keyboardDismissMode, UIScrollViewKeyboardDismissMode) +#endif // visionOS] RCT_SET_AND_PRESERVE_OFFSET(setMaximumZoomScale, maximumZoomScale, CGFloat) RCT_SET_AND_PRESERVE_OFFSET(setMinimumZoomScale, minimumZoomScale, CGFloat) #endif // [macOS] @@ -1415,7 +1417,9 @@ -(type)getter \ RCT_SET_AND_PRESERVE_OFFSET(setShowsHorizontalScrollIndicator, showsHorizontalScrollIndicator, BOOL) RCT_SET_AND_PRESERVE_OFFSET(setShowsVerticalScrollIndicator, showsVerticalScrollIndicator, BOOL) RCT_SET_AND_PRESERVE_OFFSET(setZoomScale, zoomScale, CGFloat); +#if !TARGET_OS_VISION // [visionOS] RCT_SET_AND_PRESERVE_OFFSET(setScrollIndicatorInsets, scrollIndicatorInsets, UIEdgeInsets); +#endif // [visionOS] #if !TARGET_OS_OSX // [macOS] - (void)setAutomaticallyAdjustsScrollIndicatorInsets:(BOOL)automaticallyAdjusts API_AVAILABLE(ios(13.0)) diff --git a/packages/react-native/React/Views/ScrollView/RCTScrollViewManager.h b/packages/react-native/React/Views/ScrollView/RCTScrollViewManager.h index f5a0060df8123a..90fdd7caea5283 100644 --- a/packages/react-native/React/Views/ScrollView/RCTScrollViewManager.h +++ b/packages/react-native/React/Views/ScrollView/RCTScrollViewManager.h @@ -11,7 +11,9 @@ #if !TARGET_OS_OSX // [macOS] @interface RCTConvert (UIScrollView) +#if TARGET_OS_IOS // [visionOS] + (UIScrollViewKeyboardDismissMode)UIScrollViewKeyboardDismissMode:(id)json; +#endif // [visionOS] @end #endif // [macOS] diff --git a/packages/react-native/React/Views/ScrollView/RCTScrollViewManager.m b/packages/react-native/React/Views/ScrollView/RCTScrollViewManager.m index 75e2365f5d1149..65fe5c498c57b7 100644 --- a/packages/react-native/React/Views/ScrollView/RCTScrollViewManager.m +++ b/packages/react-native/React/Views/ScrollView/RCTScrollViewManager.m @@ -12,7 +12,7 @@ #import "RCTShadowView.h" #import "RCTUIManager.h" -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] @implementation RCTConvert (UIScrollView) RCT_ENUM_CONVERTER( @@ -49,7 +49,7 @@ @implementation RCTConvert (UIScrollView) integerValue) @end -#endif // [macOS] +#endif // [macOS] [visionOS] @implementation RCTScrollViewManager @@ -72,7 +72,9 @@ - (RCTPlatformView *)view // [macOS] RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(decelerationRate, CGFloat) // [macOS] RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(directionalLockEnabled, BOOL) // [macOS] RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(indicatorStyle, UIScrollViewIndicatorStyle) // [macOS] +#if TARGET_OS_IOS // [visionOS] RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(keyboardDismissMode, UIScrollViewKeyboardDismissMode) // [macOS] +#endif // [visionOS] RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(maximumZoomScale, CGFloat) // [macOS] RCT_EXPORT_NOT_OSX_VIEW_PROPERTY(minimumZoomScale, CGFloat) // [macOS] RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL) diff --git a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm index 1c3df6c577d215..c7f403747ae98d 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm +++ b/packages/react-native/ReactCommon/react/nativemodule/samples/platform/ios/ReactCommon/RCTSampleTurboModule.mm @@ -46,8 +46,12 @@ - (NSDictionary *)getConstants __block NSDictionary *constants; RCTUnsafeExecuteOnMainQueueSync(^{ #if !TARGET_OS_OSX // [macOS] +#if !TARGET_OS_VISION // [visionOS] UIScreen *mainScreen = UIScreen.mainScreen; CGSize screenSize = mainScreen.bounds.size; +#else // [visionOS + CGSize screenSize = CGSizeZero; +#endif // visionOS] #else // [macOS NSScreen *mainScreen = NSScreen.mainScreen; CGSize screenSize = mainScreen.frame.size; diff --git a/packages/rn-tester/RNTester/AppDelegate.mm b/packages/rn-tester/RNTester/AppDelegate.mm index 00478d93eec84b..c54e2d713c80bf 100644 --- a/packages/rn-tester/RNTester/AppDelegate.mm +++ b/packages/rn-tester/RNTester/AppDelegate.mm @@ -12,15 +12,23 @@ #import #import -#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC #import -#endif #if RCT_NEW_ARCH_ENABLED #import #import #endif +#if BUNDLE_PATH +NSString *kBundlePath = @"xplat/js/RKJSModules/EntryPoints/RNTesterTestBundle.js"; +#else +#if !TARGET_OS_OSX // [macOS] +NSString *kBundlePath = @"js/RNTesterApp.ios"; +#else // [macOS +NSString *kBundlePath = @"js/RNTesterApp.macos"; +#endif // macOS] +#endif + @implementation AppDelegate #if !TARGET_OS_OSX // [macOS] @@ -92,8 +100,6 @@ - (void)loadSourceForBridge:(RCTBridge *)bridge return nullptr; } -#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC - // Required for the remoteNotificationsRegistered event. - (void)application:(__unused RCTUIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken @@ -114,14 +120,14 @@ - (void)application:(__unused RCTUIApplication *)application didReceiveRemoteNot [RCTPushNotificationManager didReceiveRemoteNotification:notification]; } -#if !TARGET_OS_OSX // [macOS] +#if TARGET_OS_IOS // [macOS] [visionOS] // Required for the localNotificationReceived event. - (void)application:(__unused UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { [RCTPushNotificationManager didReceiveLocalNotification:notification]; } -#endif // [macOS] +#endif // [macOS] [visionOS] #if TARGET_OS_OSX // [macOS - (void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification @@ -140,7 +146,6 @@ - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center return YES; } #endif // macOS] -#endif #pragma mark - RCTComponentViewFactoryComponentProvider