Skip to content

Commit 1479217

Browse files
committed
fix(ios): stub Performance and In-App Messaging when Catalyst SPM headers absent
1 parent 1b6188b commit 1479217

4 files changed

Lines changed: 126 additions & 9 deletions

File tree

okf-bundle/ios-spm-native-imports.md

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,31 @@ Adding the real per-product header branch fixes products that actually publish a
7070
usable Objective-C header. It does not fix Firebase products whose public
7171
Objective-C class interface is generated from Swift.
7272

73-
When neither the CocoaPods umbrella nor the SPM product header is present — for
74-
example Firebase App Distribution on Mac Catalyst under SPM — a `.mm`
75-
TurboModule must compile a stub that rejects at runtime with a clear
73+
When neither the CocoaPods umbrella nor the SPM product header is present, a
74+
`.mm` TurboModule must compile a stub that rejects at runtime with a clear
7675
unsupported/unavailable error. Falling through to `@import` is never allowed;
7776
do not enable `-fcxx-modules` to make the module import work.
7877

78+
### Mac Catalyst stubs (mirroring upstream `Package.swift` gates)
79+
80+
This is **not** an SPM integration bug in RNFB. Upstream
81+
`firebase-ios-sdk` `Package.swift` wrap targets omit `.macCatalyst` for some
82+
products, so Catalyst + SPM links only the empty `SwiftPM-PlatformExclude/*Wrap`
83+
dummies and never builds the real modules. CocoaPods-era Catalyst builds
84+
compiled through the `Firebase/Firebase.h` umbrella and therefore masked the
85+
same platform exclusion.
86+
87+
| Product | Upstream SPM Catalyst gate | RNFB stub | Notes |
88+
| --- | --- | --- | --- |
89+
| App Distribution | `FirebaseAppDistributionTarget` is `.iOS` only | `RNFBAppDistributionModule.mm` (`RNFB_APP_DISTRIBUTION_SDK_AVAILABLE`) | Intentionally iOS-tester-only; not proposed for Catalyst |
90+
| Performance | `FirebasePerformanceTarget` omits `.macCatalyst` | `RNFBPerfModule.mm` (`RNFB_PERF_SDK_AVAILABLE`) | Speculative enablement: [firebase-ios-sdk#16468](https://github.com/firebase/firebase-ios-sdk/pull/16468) |
91+
| In-App Messaging | `FirebaseInAppMessagingTarget` omits `.macCatalyst` | `RNFBFiamHelper.m` (`RNFB_FIAM_SDK_AVAILABLE`; Catalyst skips `@import`) | Same speculative PR; helper stays plain `.m` so iOS/tvOS SPM can `@import` |
92+
93+
Until/unless #16468 lands (or upstream declines and the stubs become permanent),
94+
RNFB must compile on Catalyst SPM without those product headers/modules. Do not
95+
stub Auth, Analytics, Crashlytics, Messaging, or other products that upstream
96+
already includes for Catalyst under SPM.
97+
7998
## Swift-product boundary
8099

81100
`FirebaseStorage`, `FirebaseRemoteConfig`, `FirebaseDatabase`, and
@@ -257,8 +276,10 @@ When native Firebase imports or SPM products change, review the diff for these
257276
invariants:
258277

259278
- no umbrella-header-to-`@import` fallback without a real product-header branch;
260-
- when product headers are absent on a platform (e.g. App Distribution on
261-
Catalyst), stub/reject in `.mm` — never `@import` and never `-fcxx-modules`;
279+
- when product headers/modules are absent on a platform because upstream SPM
280+
wrap targets omit `.macCatalyst` (App Distribution, Performance, In-App
281+
Messaging — see table above), stub/reject — never `@import` from `.mm` and
282+
never `-fcxx-modules`;
262283
- no direct Swift-product Firebase import, Firebase-typed declaration, or
263284
Firebase-bearing shared header in a `.mm` translation unit;
264285
- no `-fcxx-modules` build setting;

packages/in-app-messaging/ios/RNFBFiam/RNFBFiamHelper.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ NS_ASSUME_NONNULL_BEGIN
3030
/// breaks React Native's own jsi/React modules. Isolating all
3131
/// FirebaseInAppMessaging usage in this plain Objective-C (.m) file avoids
3232
/// that requirement entirely, since `@import` only needs `-fmodules`
33-
/// (already enabled) for plain ObjC. See okf-bundle/ios-spm-native-imports.md.
33+
/// (already enabled) for plain ObjC. On Mac Catalyst + SPM the upstream
34+
/// wrap target omits the real module — see RNFBFiamHelper.m stubs and
35+
/// https://github.com/firebase/firebase-ios-sdk/pull/16468. See
36+
/// okf-bundle/ios-spm-native-imports.md.
3437
@interface RNFBFiamHelper : NSObject
3538

3639
+ (BOOL)isMessageDisplaySuppressed;

packages/in-app-messaging/ios/RNFBFiam/RNFBFiamHelper.m

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,66 @@
1717

1818
#if __has_include(<Firebase/Firebase.h>)
1919
#import <Firebase/Firebase.h>
20-
#else
20+
#define RNFB_FIAM_SDK_AVAILABLE 1
21+
#elif !TARGET_OS_MACCATALYST
22+
// SPM on iOS/tvOS: FirebaseInAppMessaging is a Swift product — use @import in
23+
// this plain .m (needs -fmodules only). Do not move @import into the .mm
24+
// TurboModule (that would require -fcxx-modules).
2125
@import FirebaseCore;
2226
@import FirebaseInAppMessaging;
27+
#define RNFB_FIAM_SDK_AVAILABLE 1
28+
#else
29+
// Product module absent under Mac Catalyst + SPM. Upstream Package.swift omits
30+
// .macCatalyst from FirebaseInAppMessagingTarget; CocoaPods historically
31+
// masked this via the Firebase umbrella. Temporary stubs pending
32+
// https://github.com/firebase/firebase-ios-sdk/pull/16468 (or permanent if
33+
// upstream declines). When that PR lands, drop the TARGET_OS_MACCATALYST
34+
// gate and use the @import path on Catalyst too.
35+
#define RNFB_FIAM_SDK_AVAILABLE 0
2336
#endif
2437

2538
#import "RNFBFiamHelper.h"
2639

2740
@implementation RNFBFiamHelper
2841

2942
+ (BOOL)isMessageDisplaySuppressed {
43+
#if RNFB_FIAM_SDK_AVAILABLE
3044
return [FIRInAppMessaging inAppMessaging].messageDisplaySuppressed;
45+
#else
46+
return NO;
47+
#endif
3148
}
3249

3350
+ (BOOL)isAutomaticDataCollectionEnabled {
51+
#if RNFB_FIAM_SDK_AVAILABLE
3452
return [FIRInAppMessaging inAppMessaging].automaticDataCollectionEnabled;
53+
#else
54+
return NO;
55+
#endif
3556
}
3657

3758
+ (void)setAutomaticDataCollectionEnabled:(BOOL)enabled {
59+
#if RNFB_FIAM_SDK_AVAILABLE
3860
[FIRInAppMessaging inAppMessaging].automaticDataCollectionEnabled = enabled;
61+
#else
62+
(void)enabled;
63+
#endif
3964
}
4065

4166
+ (void)setMessageDisplaySuppressed:(BOOL)enabled {
67+
#if RNFB_FIAM_SDK_AVAILABLE
4268
[FIRInAppMessaging inAppMessaging].messageDisplaySuppressed = enabled;
69+
#else
70+
(void)enabled;
71+
#endif
4372
}
4473

4574
+ (void)triggerEvent:(NSString *)eventId {
75+
#if RNFB_FIAM_SDK_AVAILABLE
4676
[[FIRInAppMessaging inAppMessaging] triggerEvent:eventId];
77+
#else
78+
(void)eventId;
79+
#endif
4780
}
4881

4982
@end

packages/perf/ios/RNFBPerf/RNFBPerfModule.mm

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,21 @@
2020

2121
#if __has_include(<Firebase/Firebase.h>)
2222
#import <Firebase/Firebase.h>
23+
#define RNFB_PERF_SDK_AVAILABLE 1
2324
#elif __has_include(<FirebasePerformance/FirebasePerformance.h>)
2425
#import <FirebaseCore/FirebaseCore.h>
2526
#import <FirebasePerformance/FirebasePerformance.h>
27+
#define RNFB_PERF_SDK_AVAILABLE 1
2628
#else
27-
@import FirebaseCore;
28-
@import FirebasePerformance;
29+
// Product headers absent (typical Mac Catalyst + SPM). Upstream Package.swift
30+
// omits .macCatalyst from FirebasePerformanceTarget; CocoaPods historically
31+
// masked this via the Firebase umbrella. Temporary stubs pending
32+
// https://github.com/firebase/firebase-ios-sdk/pull/16468 (or permanent if
33+
// upstream declines). Never fall through to @import in this .mm — that
34+
// requires -fcxx-modules and breaks RN C++/JSI.
35+
#define RNFB_PERF_SDK_AVAILABLE 0
2936
#endif
37+
#import "RNFBApp/RNFBSharedUtils.h"
3038
#import "RNFBPerfModule.h"
3139

3240
static __strong NSMutableDictionary *traces;
@@ -64,12 +72,28 @@ - (void)invalidate {
6472
}
6573
}
6674

75+
#if !RNFB_PERF_SDK_AVAILABLE
76+
- (void)rejectUnavailable:(RCTPromiseRejectBlock)reject {
77+
[RNFBSharedUtils rejectPromiseWithUserInfo:reject
78+
userInfo:(NSMutableDictionary *)@{
79+
@"code" : @"unsupported",
80+
@"message" : @"Firebase Performance is not available on "
81+
@"this platform or dependency configuration.",
82+
}];
83+
}
84+
#endif
85+
6786
- (NSDictionary *)perfConstantsDictionary {
6887
NSMutableDictionary *constants = [NSMutableDictionary new];
88+
#if RNFB_PERF_SDK_AVAILABLE
6989
constants[@"isPerformanceCollectionEnabled"] =
7090
@([RCTConvert BOOL:@([FIRPerformance sharedInstance].dataCollectionEnabled)]);
7191
constants[@"isInstrumentationEnabled"] =
7292
@([RCTConvert BOOL:@([FIRPerformance sharedInstance].instrumentationEnabled)]);
93+
#else
94+
constants[@"isPerformanceCollectionEnabled"] = @(NO);
95+
constants[@"isInstrumentationEnabled"] = @(NO);
96+
#endif
7397
return constants;
7498
}
7599

@@ -89,27 +113,45 @@ - (NSDictionary *)perfConstantsDictionary {
89113
- (void)setPerformanceCollectionEnabled:(BOOL)enabled
90114
resolve:(RCTPromiseResolveBlock)resolve
91115
reject:(RCTPromiseRejectBlock)reject {
116+
#if RNFB_PERF_SDK_AVAILABLE
92117
[FIRPerformance sharedInstance].dataCollectionEnabled = (BOOL)enabled;
93118
resolve([NSNull null]);
119+
#else
120+
(void)enabled;
121+
(void)resolve;
122+
[self rejectUnavailable:reject];
123+
#endif
94124
}
95125

96126
- (void)instrumentationEnabled:(BOOL)enabled
97127
resolve:(RCTPromiseResolveBlock)resolve
98128
reject:(RCTPromiseRejectBlock)reject {
129+
#if RNFB_PERF_SDK_AVAILABLE
99130
[FIRPerformance sharedInstance].instrumentationEnabled = (BOOL)enabled;
100131
resolve([NSNull null]);
132+
#else
133+
(void)enabled;
134+
(void)resolve;
135+
[self rejectUnavailable:reject];
136+
#endif
101137
}
102138

103139
- (void)startTrace:(double)id identifier:(NSString *)identifier {
140+
#if RNFB_PERF_SDK_AVAILABLE
104141
FIRTrace *trace = [[FIRPerformance sharedInstance] traceWithName:identifier];
105142
[trace start];
106143

107144
@synchronized([self class]) {
108145
traces[@((int)id)] = trace;
109146
}
147+
#else
148+
(void)id;
149+
(void)identifier;
150+
#endif
110151
}
111152

112153
- (void)stopTrace:(double)id traceData:(JS::NativeRNFBTurboPerf::TraceData &)traceData {
154+
#if RNFB_PERF_SDK_AVAILABLE
113155
FIRTrace *trace;
114156
@synchronized([self class]) {
115157
trace = traces[@((int)id)];
@@ -132,17 +174,25 @@ - (void)stopTrace:(double)id traceData:(JS::NativeRNFBTurboPerf::TraceData &)tra
132174
@synchronized([self class]) {
133175
[traces removeObjectForKey:@((int)id)];
134176
}
177+
#else
178+
(void)id;
179+
(void)traceData;
180+
#endif
135181
}
136182

137183
- (void)startScreenTrace:(double)id identifier:(NSString *)identifier {
138184
// Custom screen traces are not supported on iOS.
185+
(void)id;
186+
(void)identifier;
139187
}
140188

141189
- (void)stopScreenTrace:(double)id {
142190
// Custom screen traces are not supported on iOS.
191+
(void)id;
143192
}
144193

145194
- (void)startHttpMetric:(double)id url:(NSString *)url httpMethod:(NSString *)httpMethod {
195+
#if RNFB_PERF_SDK_AVAILABLE
146196
FIRHTTPMethod method = FIRHTTPMethodGET;
147197
NSURL *toNSURL = [NSURL URLWithString:url];
148198
if ([httpMethod compare:@"put" options:NSCaseInsensitiveSearch] == NSOrderedSame)
@@ -168,9 +218,15 @@ - (void)startHttpMetric:(double)id url:(NSString *)url httpMethod:(NSString *)ht
168218
@synchronized([self class]) {
169219
httpMetrics[@((int)id)] = httpMetric;
170220
}
221+
#else
222+
(void)id;
223+
(void)url;
224+
(void)httpMethod;
225+
#endif
171226
}
172227

173228
- (void)stopHttpMetric:(double)id metricData:(JS::NativeRNFBTurboPerf::HttpMetricData &)metricData {
229+
#if RNFB_PERF_SDK_AVAILABLE
174230
FIRHTTPMetric *httpMetric;
175231
@synchronized([self class]) {
176232
httpMetric = httpMetrics[@((int)id)];
@@ -203,6 +259,10 @@ - (void)stopHttpMetric:(double)id metricData:(JS::NativeRNFBTurboPerf::HttpMetri
203259
@synchronized([self class]) {
204260
[httpMetrics removeObjectForKey:@((int)id)];
205261
}
262+
#else
263+
(void)id;
264+
(void)metricData;
265+
#endif
206266
}
207267

208268
@end

0 commit comments

Comments
 (0)