diff --git a/.changeset/focus-zone-i18n-header.md b/.changeset/focus-zone-i18n-header.md new file mode 100644 index 00000000000..f7e9046c0c9 --- /dev/null +++ b/.changeset/focus-zone-i18n-header.md @@ -0,0 +1,5 @@ +--- +'@fluentui-react-native/native-core': patch +--- + +Import `RCTI18nUtil.h` in the macOS FocusZone implementation with the canonical `` form instead of by bare, differently cased header name. diff --git a/.changeset/focus-zone-unused-params.md b/.changeset/focus-zone-unused-params.md new file mode 100644 index 00000000000..984783db307 --- /dev/null +++ b/.changeset/focus-zone-unused-params.md @@ -0,0 +1,5 @@ +--- +'@fluentui-react-native/native-core': patch +--- + +Mark the unused block parameters in the macOS FocusZone traversal as `__unused` so the file compiles cleanly with `-Wunused-parameter` treated as an error. diff --git a/.changeset/optional-react-import.md b/.changeset/optional-react-import.md new file mode 100644 index 00000000000..120c65b601c --- /dev/null +++ b/.changeset/optional-react-import.md @@ -0,0 +1,6 @@ +--- +'@fluentui-react-native/experimental-avatar': patch +'@fluentui-react-native/experimental-native-date-picker': patch +--- + +Import `React` in iOS Swift sources only when the module is available, so hosts can provide React Native headers through an Objective-C bridging header. diff --git a/.changeset/qualified-react-headers.md b/.changeset/qualified-react-headers.md new file mode 100644 index 00000000000..6dbd7d3ce8a --- /dev/null +++ b/.changeset/qualified-react-headers.md @@ -0,0 +1,6 @@ +--- +'@fluentui-react-native/experimental-appearance-additions': patch +'@fluentui-react-native/experimental-native-date-picker': patch +--- + +Import React Native headers in iOS Objective-C sources with the canonical `` form instead of by bare header name. diff --git a/.changeset/swift-callout-commands.md b/.changeset/swift-callout-commands.md new file mode 100644 index 00000000000..f603673b611 --- /dev/null +++ b/.changeset/swift-callout-commands.md @@ -0,0 +1,5 @@ +--- +'@fluentui-react-native/native-core': patch +--- + +Implement the macOS Paper Callout `focusWindow` and `blurWindow` commands in Swift so the Objective-C manager no longer imports the generated Swift header, which duplicates the manager's interface when hosts compile Swift with an Objective-C bridging header. diff --git a/packages/agentic/native-core/README.md b/packages/agentic/native-core/README.md index 94f76086e5f..bd101489108 100644 --- a/packages/agentic/native-core/README.md +++ b/packages/agentic/native-core/README.md @@ -40,7 +40,11 @@ to use its host-provided `RCTCallout` and `RCTFocusZone`. The macOS module-named header at `macos/shared/FRNNativeCore.h` is required by the generated `FRNNativeCore-Swift.h`; keep it aligned with the pod's -`module_name` when changing native ownership. +`module_name` when changing native ownership. Only Fabric adapters import the +generated header. Hosts that compile the Swift sources with an Objective-C +bridging header also get internal Swift classes declared there, duplicating +Paper `RCT_EXTERN_MODULE` interfaces, so implement Paper manager methods in +Swift and declare them with `RCT_EXTERN_METHOD`. ## Public entrypoints diff --git a/packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift b/packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift index 89350cd109e..739986c194c 100644 --- a/packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift +++ b/packages/agentic/native-core/macos/components/Callout/paper/CalloutManager.swift @@ -13,4 +13,19 @@ class CalloutManager: RCTViewManager { override class func requiresMainQueueSetup() -> Bool { return true } + + // React Native calls these on the module's method queue; views can only be looked up on the main queue. + @objc func focusWindow(_ viewTag: NSNumber) { + nonisolated(unsafe) let manager = self + DispatchQueue.main.async { + (manager.bridge?.uiManager?.view(forReactTag: viewTag) as? CalloutView)?.focusWindow() + } + } + + @objc func blurWindow(_ viewTag: NSNumber) { + nonisolated(unsafe) let manager = self + DispatchQueue.main.async { + (manager.bridge?.uiManager?.view(forReactTag: viewTag) as? CalloutView)?.blurWindow() + } + } } diff --git a/packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m b/packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m index f162f6cc3b3..49d3ae6dae3 100644 --- a/packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m +++ b/packages/agentic/native-core/macos/components/Callout/paper/FRNCalloutManager.m @@ -1,7 +1,5 @@ #import "FRNCalloutManager.h" -#import "FRNNativeCore-Swift.h" - @implementation RCTConvert (FRNCalloutAdditions) // RCTConvert does not properly convert a JS screenRect into a native CGRect/NSRect, @@ -39,27 +37,9 @@ + (NSRect)screenRect:(id)json @interface RCT_EXTERN_MODULE(RCTCalloutManager, RCTViewManager) -RCT_EXPORT_METHOD(focusWindow : (nonnull NSNumber *)viewTag) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - NSView *view = [self.bridge.uiManager viewForReactTag:viewTag]; - - if ([view isKindOfClass:[FRNCalloutView class]]) { - [(FRNCalloutView *)view focusWindow]; - } - }); -} +RCT_EXTERN_METHOD(focusWindow : (nonnull NSNumber *)viewTag) -RCT_EXPORT_METHOD(blurWindow : (nonnull NSNumber *)viewTag) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - NSView *view = [self.bridge.uiManager viewForReactTag:viewTag]; - - if ([view isKindOfClass:[FRNCalloutView class]]) { - [(FRNCalloutView *)view blurWindow]; - } - }); -} +RCT_EXTERN_METHOD(blurWindow : (nonnull NSNumber *)viewTag) RCT_EXPORT_VIEW_PROPERTY(target, NSNumber) diff --git a/packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m b/packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m index e3582553848..28100c9f17b 100644 --- a/packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m +++ b/packages/agentic/native-core/macos/components/FocusZone/shared/RCTFocusZone.m @@ -1,7 +1,7 @@ #import "KeyCodes.h" #import #import "RCTFocusZone.h" -#import "RCTi18nUtil.h" +#import typedef enum { FocusZoneActionNone, @@ -560,7 +560,7 @@ - (NSView *)nextViewToFocusInHierarchyForAction:(FocusZoneAction)action AppendFocusableViewsWithin(self, focusableViews); NSView *firstResponder = GetFirstResponder([self window]); - NSUInteger currentIndex = [focusableViews indexOfObjectPassingTest:^BOOL(NSView *candidateView, NSUInteger index, BOOL *stop) { + NSUInteger currentIndex = [focusableViews indexOfObjectPassingTest:^BOOL(NSView *candidateView, __unused NSUInteger index, __unused BOOL *stop) { return candidateView == firstResponder || [candidateView isDescendantOf:firstResponder] || [firstResponder isDescendantOf:candidateView]; diff --git a/packages/agentic/native-core/tests/native-ownership.test.mjs b/packages/agentic/native-core/tests/native-ownership.test.mjs index ceb5ab99cea..38bdf30322d 100644 --- a/packages/agentic/native-core/tests/native-ownership.test.mjs +++ b/packages/agentic/native-core/tests/native-ownership.test.mjs @@ -53,10 +53,18 @@ test('macOS Swift interoperability uses the consolidated pod module name', async assert.equal(moduleName, 'FRNNativeCore'); await readFile(new URL(`macos/shared/${moduleName}.h`, packageRoot), 'utf8'); - for (const adapter of ['fabric/RCTCalloutComponentView.mm', 'paper/FRNCalloutManager.m']) { - const source = await readFile(new URL(`macos/components/Callout/${adapter}`, packageRoot), 'utf8'); - assert.ok(source.includes(`#import "${moduleName}-Swift.h"`)); - assert.doesNotMatch(source, /FRNCallout-Swift\.h/); + const fabric = await readFile(new URL('macos/components/Callout/fabric/RCTCalloutComponentView.mm', packageRoot), 'utf8'); + assert.ok(fabric.includes(`#import "${moduleName}-Swift.h"`)); + assert.doesNotMatch(fabric, /FRNCallout-Swift\.h/); + + // Bridging-header builds also declare internal Swift classes in the generated header, duplicating the Paper + // manager's RCT_EXTERN_MODULE interface, so Paper commands are implemented in Swift instead. + const paper = await readFile(new URL('macos/components/Callout/paper/FRNCalloutManager.m', packageRoot), 'utf8'); + const manager = await readFile(new URL('macos/components/Callout/paper/CalloutManager.swift', packageRoot), 'utf8'); + assert.doesNotMatch(paper, /-Swift\.h/); + for (const command of ['focusWindow', 'blurWindow']) { + assert.match(paper, new RegExp(`RCT_EXTERN_METHOD\\(${command} : \\(nonnull NSNumber \\*\\)viewTag\\)`)); + assert.match(manager, new RegExp(`@objc func ${command}\\(_ viewTag: NSNumber\\)`)); } }); diff --git a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m index 9adafc69d31..84f3aedc97e 100644 --- a/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m +++ b/packages/experimental/AppearanceAdditions/ios/FRNAppearanceAdditions.m @@ -1,5 +1,5 @@ #import "FRNAppearanceAdditions.h" -#import "RCTUIManager.h" +#import #import #import diff --git a/packages/experimental/Avatar/ios/AvatarViewManager.swift b/packages/experimental/Avatar/ios/AvatarViewManager.swift index 0221f0784e9..ef862e8278f 100644 --- a/packages/experimental/Avatar/ios/AvatarViewManager.swift +++ b/packages/experimental/Avatar/ios/AvatarViewManager.swift @@ -1,6 +1,8 @@ import Foundation import FluentUI +#if canImport(React) import React +#endif import UIKit @objc(FRNAvatarViewManager) diff --git a/packages/experimental/NativeDatePicker/ios/DatePickerDelegate.swift b/packages/experimental/NativeDatePicker/ios/DatePickerDelegate.swift index 65336e737f4..d48f74b1659 100644 --- a/packages/experimental/NativeDatePicker/ios/DatePickerDelegate.swift +++ b/packages/experimental/NativeDatePicker/ios/DatePickerDelegate.swift @@ -1,6 +1,8 @@ import FluentUI import Foundation +#if canImport(React) import React +#endif class DatePickerDelegate: DateTimePickerDelegate { weak var manager: DatePickerManager? diff --git a/packages/experimental/NativeDatePicker/ios/DatePickerManager.swift b/packages/experimental/NativeDatePicker/ios/DatePickerManager.swift index 3a5efb03a9f..8e503fbf853 100644 --- a/packages/experimental/NativeDatePicker/ios/DatePickerManager.swift +++ b/packages/experimental/NativeDatePicker/ios/DatePickerManager.swift @@ -1,6 +1,8 @@ import FluentUI import Foundation +#if canImport(React) import React +#endif @objc(FRNDatePickerManager) public class DatePickerManager: NSObject { diff --git a/packages/experimental/NativeDatePicker/ios/FRNDatePickerManager.m b/packages/experimental/NativeDatePicker/ios/FRNDatePickerManager.m index 77d8ad3d361..a638f1da420 100644 --- a/packages/experimental/NativeDatePicker/ios/FRNDatePickerManager.m +++ b/packages/experimental/NativeDatePicker/ios/FRNDatePickerManager.m @@ -1,4 +1,4 @@ -#import "RCTBridgeModule.h" +#import #import #import #import