Skip to content

Commit d4f3d71

Browse files
fix(ios): guard +load with #ifdef RCT_DYNAMIC_FRAMEWORKS (#3828)
## Summary 12 ComponentView files had unconditional `+load { [super load]; }` methods, but their parent `RCTViewComponentView` correctly guards its `+load` behind `#ifdef RCT_DYNAMIC_FRAMEWORKS`. Without the guard, these `+load` methods run even when dynamic frameworks are not used, causing conflicts with third-party SDKs (e.g. Akamai BMP) that also use `+load` — particularly on x86_64 simulators where the load order differs from arm64. This matches the pattern used by React Native core in `RCTViewComponentView.mm` (see facebook/react-native#37274). **Note:** `UIViewController+RNScreens.mm` is intentionally NOT changed as its `+load` performs method swizzling that is functionally required. ### Changes 1. **Guarded existing `+load` methods** — wrapped 12 existing unconditional `+load` calls with `#ifdef RCT_DYNAMIC_FRAMEWORKS`. 2. **Added missing guarded `+load` methods** — 9 additional ComponentView files (gamma, tabs) were missing `+load` entirely, which would break component registration when using dynamic frameworks. Added guarded `+load` to these files. 3. **Relocated `+load` to end of `@implementation`** — moved all `+load` methods to a dedicated `#pragma mark - Dynamic frameworks support` section just before `@end` for consistency and readability. 4. **Normalized `#endif` comments** — ensured all `#endif` directives have consistent trailing comments (e.g. `#endif // RCT_DYNAMIC_FRAMEWORKS`). ### Affected files **Existing `+load` guarded (12 files):** - `RNSScreen.mm` - `RNSScreenStack.mm` - `RNSScreenContainer.mm` - `RNSScreenNavigationContainer.mm` - `RNSModalScreen.mm` - `RNSFullWindowOverlay.mm` - `RNSScreenFooter.mm` - `RNSSafeAreaViewComponentView.mm` - `RNSSearchBar.mm` - `RNSScreenContentWrapper.mm` - `RNSScreenStackHeaderConfig.mm` - `RNSScreenStackHeaderSubview.mm` **New guarded `+load` added (9 files):** - `RNSScrollViewMarkerComponentView.mm` - `RNSSplitHostComponentView.mm` - `RNSSplitScreenComponentView.mm` - `RNSStackHostComponentView.mm` - `RNSStackScreenComponentView.mm` - `RNSTabsHostComponentView.mm` - `RNSTabsScreenComponentView.mm` - `RNSTabsBottomAccessoryComponentView.mm` - `RNSTabsBottomAccessoryContentComponentView.mm` ## Test plan - Build xcframework with static linking (no `USE_FRAMEWORKS=dynamic`) - Verify ComponentView `+load` methods are not in the binary (`nm` check) - Run on x86_64 simulator alongside Akamai BMP — no crash at launch - Run on arm64 simulator — screens navigation works normally --------- Co-authored-by: Kacper Kafara <kacperkafara@gmail.com>
1 parent 33feebb commit d4f3d71

21 files changed

Lines changed: 198 additions & 59 deletions

ios/RNSFullWindowOverlay.mm

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,6 @@ @implementation RNSFullWindowOverlay {
7171
RCTSurfaceTouchHandler *_touchHandler;
7272
}
7373

74-
// Needed because of this: https://github.com/facebook/react-native/pull/37274
75-
+ (void)load
76-
{
77-
[super load];
78-
}
79-
8074
- (instancetype)init
8175
{
8276
if (self = [super init]) {
@@ -227,6 +221,16 @@ - (void)updateProps:(const facebook::react::Props::Shared &)props
227221
[super updateProps:props oldProps:oldProps];
228222
}
229223

224+
#pragma mark - Dynamic frameworks support
225+
226+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
227+
#ifdef RCT_DYNAMIC_FRAMEWORKS
228+
+ (void)load
229+
{
230+
[super load];
231+
}
232+
#endif // RCT_DYNAMIC_FRAMEWORKS
233+
230234
@end
231235

232236
Class<RCTComponentViewProtocol> RNSFullWindowOverlayCls(void)

ios/RNSModalScreen.mm

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,20 @@ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
2727
}];
2828
}
2929

30-
// Needed because of this: https://github.com/facebook/react-native/pull/37274
31-
+ (void)load
30+
+ (react::ComponentDescriptorProvider)componentDescriptorProvider
3231
{
33-
[super load];
32+
return react::concreteComponentDescriptorProvider<react::RNSModalScreenComponentDescriptor>();
3433
}
3534

36-
+ (react::ComponentDescriptorProvider)componentDescriptorProvider
35+
#pragma mark - Dynamic frameworks support
36+
37+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
38+
#ifdef RCT_DYNAMIC_FRAMEWORKS
39+
+ (void)load
3740
{
38-
return react::concreteComponentDescriptorProvider<react::RNSModalScreenComponentDescriptor>();
41+
[super load];
3942
}
43+
#endif // RCT_DYNAMIC_FRAMEWORKS
4044

4145
@end
4246

ios/RNSScreen.mm

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,6 @@ @implementation RNSScreenView {
6767
NSMutableArray<UIView *> *_reactSubviews;
6868
}
6969

70-
// Needed because of this: https://github.com/facebook/react-native/pull/37274
71-
+ (void)load
72-
{
73-
[super load];
74-
}
75-
7670
- (instancetype)initWithFrame:(CGRect)frame
7771
{
7872
if (self = [super initWithFrame:frame]) {
@@ -1432,6 +1426,16 @@ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask
14321426
#endif // !TARGET_OS_TV && !TARGET_OS_VISION
14331427
}
14341428

1429+
#pragma mark - Dynamic frameworks support
1430+
1431+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
1432+
#ifdef RCT_DYNAMIC_FRAMEWORKS
1433+
+ (void)load
1434+
{
1435+
[super load];
1436+
}
1437+
#endif // RCT_DYNAMIC_FRAMEWORKS
1438+
14351439
@end
14361440

14371441
Class<RCTComponentViewProtocol> RNSScreenCls(void)

ios/RNSScreenContainer.mm

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,6 @@ - (void)layoutSubviews
250250

251251
#pragma mark-- Fabric specific
252252

253-
// Needed because of this: https://github.com/facebook/react-native/pull/37274
254-
+ (void)load
255-
{
256-
[super load];
257-
}
258-
259253
#pragma mark - RCTComponentViewProtocol
260254

261255
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
@@ -316,6 +310,17 @@ - (void)prepareForRecycle
316310
[_controller willMoveToParentViewController:nil];
317311
[_controller removeFromParentViewController];
318312
}
313+
314+
#pragma mark - Dynamic frameworks support
315+
316+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
317+
#ifdef RCT_DYNAMIC_FRAMEWORKS
318+
+ (void)load
319+
{
320+
[super load];
321+
}
322+
#endif // RCT_DYNAMIC_FRAMEWORKS
323+
319324
@end
320325

321326
Class<RCTComponentViewProtocol> RNSScreenContainerCls(void)

ios/RNSScreenContentWrapper.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,15 @@ - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetric
183183
return RNSScreenContentWrapper.class;
184184
}
185185

186+
#pragma mark - Dynamic frameworks support
187+
186188
// Needed because of this: https://github.com/facebook/react-native/pull/37274
189+
#ifdef RCT_DYNAMIC_FRAMEWORKS
187190
+ (void)load
188191
{
189192
[super load];
190193
}
194+
#endif // RCT_DYNAMIC_FRAMEWORKS
191195

192196
@end
193197

ios/RNSScreenFooter.mm

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,6 @@ - (void)layoutSubviews
9090
// }
9191
}
9292

93-
// Needed because of this: https://github.com/facebook/react-native/pull/37274
94-
+ (void)load
95-
{
96-
[super load];
97-
}
98-
9993
+ (react::ComponentDescriptorProvider)componentDescriptorProvider
10094
{
10195
return react::concreteComponentDescriptorProvider<react::RNSScreenFooterComponentDescriptor>();
@@ -106,6 +100,16 @@ + (void)load
106100
return RNSScreenFooter.class;
107101
}
108102

103+
#pragma mark - Dynamic frameworks support
104+
105+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
106+
#ifdef RCT_DYNAMIC_FRAMEWORKS
107+
+ (void)load
108+
{
109+
[super load];
110+
}
111+
#endif // RCT_DYNAMIC_FRAMEWORKS
112+
109113
@end
110114

111115
@implementation RNSScreenFooterManager

ios/RNSScreenNavigationContainer.mm

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,15 @@ - (void)updateContainer
4242
return react::concreteComponentDescriptorProvider<react::RNSScreenNavigationContainerComponentDescriptor>();
4343
}
4444

45+
#pragma mark - Dynamic frameworks support
46+
4547
// Needed because of this: https://github.com/facebook/react-native/pull/37274
48+
#ifdef RCT_DYNAMIC_FRAMEWORKS
4649
+ (void)load
4750
{
4851
[super load];
4952
}
53+
#endif // RCT_DYNAMIC_FRAMEWORKS
5054

5155
@end
5256

ios/RNSScreenStack.mm

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,6 @@ @implementation RNSScreenStackView {
181181
UIPanGestureRecognizer *_sinkEventsPanGestureRecognizer;
182182
}
183183

184-
// Needed because of this: https://github.com/facebook/react-native/pull/37274
185-
+ (void)load
186-
{
187-
[super load];
188-
}
189-
190184
- (instancetype)initWithFrame:(CGRect)frame
191185
{
192186
if (self = [super initWithFrame:frame]) {
@@ -1434,6 +1428,16 @@ - (void)prepareForRecycle
14341428
return react::concreteComponentDescriptorProvider<react::RNSScreenStackComponentDescriptor>();
14351429
}
14361430

1431+
#pragma mark - Dynamic frameworks support
1432+
1433+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
1434+
#ifdef RCT_DYNAMIC_FRAMEWORKS
1435+
+ (void)load
1436+
{
1437+
[super load];
1438+
}
1439+
#endif // RCT_DYNAMIC_FRAMEWORKS
1440+
14371441
@end
14381442

14391443
Class<RCTComponentViewProtocol> RNSScreenStackCls(void)

ios/RNSScreenStackHeaderConfig.mm

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,6 @@ @implementation RNSScreenStackHeaderConfig {
6262
RCTImageLoader *_imageLoader;
6363
}
6464

65-
// Needed because of this: https://github.com/facebook/react-native/pull/37274
66-
+ (void)load
67-
{
68-
[super load];
69-
}
70-
7165
- (instancetype)initWithFrame:(CGRect)frame
7266
{
7367
if (self = [super initWithFrame:frame]) {
@@ -1113,6 +1107,16 @@ - (void)updateState:(const facebook::react::State::Shared &)state
11131107
}
11141108
}
11151109

1110+
#pragma mark - Dynamic frameworks support
1111+
1112+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
1113+
#ifdef RCT_DYNAMIC_FRAMEWORKS
1114+
+ (void)load
1115+
{
1116+
[super load];
1117+
}
1118+
#endif // RCT_DYNAMIC_FRAMEWORKS
1119+
11161120
@end
11171121

11181122
Class<RCTComponentViewProtocol> RNSScreenStackHeaderConfigCls(void)

ios/RNSScreenStackHeaderSubview.mm

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,6 @@ - (void)layoutSubviews
110110
[self updateShadowStateInContextOfAncestorView:[self findNavigationBar]];
111111
}
112112

113-
// Needed because of this: https://github.com/facebook/react-native/pull/37274
114-
+ (void)load
115-
{
116-
[super load];
117-
}
118-
119113
- (instancetype)initWithFrame:(CGRect)frame
120114
{
121115
if (self = [super initWithFrame:frame]) {
@@ -299,6 +293,16 @@ - (void)setHidesSharedBackground:(BOOL)hidesSharedBackground
299293
[self configureBarButtonItem];
300294
}
301295

296+
#pragma mark - Dynamic frameworks support
297+
298+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
299+
#ifdef RCT_DYNAMIC_FRAMEWORKS
300+
+ (void)load
301+
{
302+
[super load];
303+
}
304+
#endif // RCT_DYNAMIC_FRAMEWORKS
305+
302306
@end
303307

304308
@implementation RNSScreenStackHeaderSubviewManager

0 commit comments

Comments
 (0)