From edb768ac4f32e64235cf09e4cd01ec32dc362bad Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Sun, 22 Mar 2026 23:00:46 -0500 Subject: [PATCH] fix(macos): fix pre-existing macOS porting bugs for SPM compilation - RCTLinkingManager.mm: combine iOS and macOS implementations into a single file using #if TARGET_OS_OSX guards, remove macos/ overlay directory, and clean up podspec - RCTCursor.m: replace Foundation + conditional AppKit imports with RCTUIKit umbrella header - RCTViewComponentView.mm: remove duplicate cursor property check introduced during merge Co-Authored-By: Claude Opus 4.6 --- .../Libraries/LinkingIOS/RCTLinkingManager.mm | 119 +++++++++++++++++ .../LinkingIOS/React-RCTLinking.podspec | 4 - .../LinkingIOS/macos/RCTLinkingManager.mm | 123 ------------------ .../View/RCTViewComponentView.mm | 5 - packages/react-native/React/Views/RCTCursor.m | 2 +- 5 files changed, 120 insertions(+), 133 deletions(-) delete mode 100644 packages/react-native/Libraries/LinkingIOS/macos/RCTLinkingManager.mm diff --git a/packages/react-native/Libraries/LinkingIOS/RCTLinkingManager.mm b/packages/react-native/Libraries/LinkingIOS/RCTLinkingManager.mm index f0c943f6aa30..16243f3cc84f 100644 --- a/packages/react-native/Libraries/LinkingIOS/RCTLinkingManager.mm +++ b/packages/react-native/Libraries/LinkingIOS/RCTLinkingManager.mm @@ -14,6 +14,8 @@ #import "RCTLinkingPlugins.h" +#if !TARGET_OS_OSX // [macOS] + static NSString *const kOpenURLNotification = @"RCTOpenURLNotification"; static void postNotificationWithURL(NSURL *URL, id sender) @@ -22,6 +24,24 @@ static void postNotificationWithURL(NSURL *URL, id sender) [[NSNotificationCenter defaultCenter] postNotificationName:kOpenURLNotification object:sender userInfo:payload]; } +#else // [macOS + +NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification"; + +static NSString *initialURL = nil; +static BOOL moduleInitalized = NO; +static BOOL alwaysForegroundLastWindow = YES; + +static void postNotificationWithURL(NSString *url, id sender) +{ + NSDictionary *payload = @{@"url": url}; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification + object:sender + userInfo:payload]; +} + +#endif // macOS] + @interface RCTLinkingManager () @end @@ -34,6 +54,8 @@ - (dispatch_queue_t)methodQueue return dispatch_get_main_queue(); } +#if !TARGET_OS_OSX // [macOS] + - (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self @@ -47,11 +69,33 @@ - (void)stopObserving [[NSNotificationCenter defaultCenter] removeObserver:self]; } +#else // [macOS + +- (void)startObserving +{ + moduleInitalized = YES; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleOpenURLNotification:) + name:RCTOpenURLNotification + object:nil]; +} + +- (void)stopObserving +{ + moduleInitalized = NO; + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +#endif // macOS] + - (NSArray *)supportedEvents { return @[ @"url" ]; } +#if !TARGET_OS_OSX // [macOS] + + (BOOL)application:(UIApplication *)app openURL:(NSURL *)URL options:(NSDictionary *)options @@ -87,6 +131,43 @@ - (void)handleOpenURLNotification:(NSNotification *)notification [self sendEventWithName:@"url" body:notification.userInfo]; } +#else // [macOS + ++ (void)setAlwaysForegroundLastWindow:(BOOL)alwaysForeground +{ + alwaysForegroundLastWindow = alwaysForeground; +} + ++ (void)getUrlEventHandler:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent +{ + // extract url value from the event + NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + + // If the application was launched via URL, this handler will be called before + // the module is initialized by the bridge. Store the initial URL, because we are not listening to the notification yet. + if (!moduleInitalized && initialURL == nil) { + initialURL = url; + } + + postNotificationWithURL(url, self); +} + +- (void)handleOpenURLNotification:(NSNotification *)notification +{ + // Activate app, because [NSApp mainWindow] returns nil when the app is hidden and another app is maximized + [NSApp activateIgnoringOtherApps:YES]; + // foreground top level window + if (alwaysForegroundLastWindow) { + NSWindow *lastWindow = [[NSApp windows] lastObject]; + [lastWindow makeKeyAndOrderFront:nil]; + } + [self sendEventWithName:@"url" body:notification.userInfo]; +} + +#endif // macOS] + +#if !TARGET_OS_OSX // [macOS] + RCT_EXPORT_METHOD(openURL : (NSURL *)URL resolve : (RCTPromiseResolveBlock)resolve reject @@ -184,6 +265,44 @@ - (void)handleOpenURLNotification:(NSNotification *)notification }]; } +#else // [macOS + +RCT_EXPORT_METHOD(openURL:(NSURL *)URL + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + BOOL result = [[NSWorkspace sharedWorkspace] openURL:URL]; + if (result) { + resolve(@YES); + } else { + reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unable to open URL: %@", URL], nil); + } +} + +RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL + resolve:(RCTPromiseResolveBlock)resolve + reject:(__unused RCTPromiseRejectBlock)reject) +{ + resolve(@YES); +} + +RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve + reject:(__unused RCTPromiseRejectBlock)reject) +{ + resolve(RCTNullIfNil(initialURL)); +} + +RCT_EXPORT_METHOD(openSettings:(RCTPromiseResolveBlock)resolve + reject:(__unused RCTPromiseRejectBlock)reject) +{ + // macOS doesn't have a direct equivalent of UIApplicationOpenSettingsURLString + // Open System Preferences instead + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:"]]; + resolve(nil); +} + +#endif // macOS] + RCT_EXPORT_METHOD(sendIntent : (NSString *)action extras : (NSArray *_Nullable)extras resolve diff --git a/packages/react-native/Libraries/LinkingIOS/React-RCTLinking.podspec b/packages/react-native/Libraries/LinkingIOS/React-RCTLinking.podspec index a359bb874781..325c2f0df621 100644 --- a/packages/react-native/Libraries/LinkingIOS/React-RCTLinking.podspec +++ b/packages/react-native/Libraries/LinkingIOS/React-RCTLinking.podspec @@ -32,10 +32,6 @@ Pod::Spec.new do |s| s.compiler_flags = '-Wno-nullability-completeness' s.source = source s.source_files = podspec_sources("*.{m,mm}", "") -# [macOS - s.osx.exclude_files = "RCTLinkingManager.mm" - s.osx.source_files = "macos/RCTLinkingManager.mm" -# macOS] s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs" s.header_dir = "RCTLinking" s.pod_target_xcconfig = { diff --git a/packages/react-native/Libraries/LinkingIOS/macos/RCTLinkingManager.mm b/packages/react-native/Libraries/LinkingIOS/macos/RCTLinkingManager.mm deleted file mode 100644 index 6b0108d8da3f..000000000000 --- a/packages/react-native/Libraries/LinkingIOS/macos/RCTLinkingManager.mm +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// [macOS] - -#import "RCTLinkingManager.h" - -#import - -#import -#import -#import - -#import "RCTLinkingPlugins.h" - -NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification"; - -static NSString *initialURL = nil; -static BOOL moduleInitalized = NO; -static BOOL alwaysForegroundLastWindow = YES; - -static void postNotificationWithURL(NSString *url, id sender) -{ - NSDictionary *payload = @{@"url": url}; - [[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification - object:sender - userInfo:payload]; -} - -@implementation RCTLinkingManager - -RCT_EXPORT_MODULE() - -- (dispatch_queue_t)methodQueue -{ - return dispatch_get_main_queue(); -} - -- (void)startObserving -{ - moduleInitalized = YES; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleOpenURLNotification:) - name:RCTOpenURLNotification - object:nil]; -} - -- (void)stopObserving -{ - moduleInitalized = NO; - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (NSArray *)supportedEvents -{ - return @[@"url"]; -} - -+ (void)setAlwaysForegroundLastWindow:(BOOL)alwaysForeground -{ - alwaysForegroundLastWindow = alwaysForeground; -} - -+ (void)getUrlEventHandler:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent -{ - // extract url value from the event - NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; - - // If the application was launched via URL, this handler will be called before - // the module is initialized by the bridge. Store the initial URL, becase we are not listening to the notification yet. - if (!moduleInitalized && initialURL == nil) { - initialURL = url; - } - - postNotificationWithURL(url, self); -} - -- (void)handleOpenURLNotification:(NSNotification *)notification -{ - // Activate app, because [NSApp mainWindow] returns nil when the app is hidden and another app is maximized - [NSApp activateIgnoringOtherApps:YES]; - // foreground top level window - if (alwaysForegroundLastWindow) { - NSWindow *lastWindow = [[NSApp windows] lastObject]; - [lastWindow makeKeyAndOrderFront:nil]; - } - [self sendEventWithName:@"url" body:notification.userInfo]; -} - -RCT_EXPORT_METHOD(openURL:(NSURL *)URL - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject) -{ - BOOL result = [[NSWorkspace sharedWorkspace] openURL:URL]; - if (result) { - resolve(@YES); - } else { - reject(RCTErrorUnspecified, [NSString stringWithFormat:@"Unable to open URL: %@", URL], nil); - } -} - -RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL - resolve:(RCTPromiseResolveBlock)resolve - reject:(__unused RCTPromiseRejectBlock)reject) -{ - resolve(@YES); -} - -RCT_EXPORT_METHOD(getInitialURL:(RCTPromiseResolveBlock)resolve - reject:(__unused RCTPromiseRejectBlock)reject) -{ - resolve(RCTNullIfNil(initialURL)); -} -@end - -Class RCTLinkingManagerCls(void) { - return RCTLinkingManager.class; -} diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 78b616a03a7a..e858d506c15b 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -341,11 +341,6 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & needsInvalidateLayer = YES; } - // `cursor` - if (oldViewProps.cursor != newViewProps.cursor) { - needsInvalidateLayer = YES; - } - // `shouldRasterize` if (oldViewProps.shouldRasterize != newViewProps.shouldRasterize) { self.layer.shouldRasterize = newViewProps.shouldRasterize; diff --git a/packages/react-native/React/Views/RCTCursor.m b/packages/react-native/React/Views/RCTCursor.m index 05575465fbef..9a959cdcb15d 100644 --- a/packages/react-native/React/Views/RCTCursor.m +++ b/packages/react-native/React/Views/RCTCursor.m @@ -7,7 +7,7 @@ // [macOS] -#import +#import #import "RCTCursor.h" #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 150000 /* __MAC_15_0 */