Summary
When an iOS app has multiple targets (e.g., main app + ShareExtension), RCTThirdPartyComponentsProvider.mm crashes because it uses NSDictionary literal syntax that cannot handle nil values.
Environment
- React Native: 0.81.5
- Expo SDK: 54
- New Architecture: Enabled
- iOS targets: Main app + ShareExtension
Problem
Crash
NSInvalidArgumentException: attempt to insert nil object from objects[14]
+[RCTThirdPartyComponentsProvider thirdPartyFabricComponents]
Root Cause
- Codegen generates a single
RCTThirdPartyComponentsProvider.mm shared between all targets
- It uses
NSDictionary literal syntax:
thirdPartyComponents = @{
@"RNGoogleSignInButton": NSClassFromString(@"RNGoogleSignInButtonComponentView"),
@"RNGoogleMobileAdsBannerView": NSClassFromString(@"RNGoogleMobileAdsBannerView"),
// ...
};
- ShareExtension/App Clip targets don't link all libraries (size constraints, API limitations)
NSClassFromString() returns nil for unlinked classes
NSDictionary literal crashes on nil values
Why existing fix doesn't help
PR #51838 (backported to 0.79, included in 0.81) allows disabling libraries via react-native.config.js. But this doesn't help when:
- Library is enabled for main app
- Library is not linked to secondary target (ShareExtension)
- Both targets share the same codegen output
Proposed Solution
Generate nil-safe code by default:
static inline void SafeAddComponent(NSMutableDictionary *dict, NSString *name, NSString *className) {
Class cls = NSClassFromString(className);
if (cls != nil) {
dict[name] = cls;
}
}
// ...
NSMutableDictionary *components = [NSMutableDictionary dictionary];
SafeAddComponent(components, @"RNGoogleSignInButton", @"RNGoogleSignInButtonComponentView");
SafeAddComponent(components, @"RNGoogleMobileAdsBannerView", @"RNGoogleMobileAdsBannerView");
thirdPartyComponents = [components copy];
This is backward compatible and handles all multi-target scenarios.
Alternative Solutions
- Generate target-specific
RCTThirdPartyComponentsProvider files
- Add a codegen option to enable nil-safe generation
Related Issues
Workaround
Currently using a post-install script to patch the generated file:
MaxAst/expo-share-extension#117 (comment)
Summary
When an iOS app has multiple targets (e.g., main app + ShareExtension),
RCTThirdPartyComponentsProvider.mmcrashes because it usesNSDictionaryliteral syntax that cannot handle nil values.Environment
Problem
Crash
Root Cause
RCTThirdPartyComponentsProvider.mmshared between all targetsNSDictionaryliteral syntax:thirdPartyComponents = @{ @"RNGoogleSignInButton": NSClassFromString(@"RNGoogleSignInButtonComponentView"), @"RNGoogleMobileAdsBannerView": NSClassFromString(@"RNGoogleMobileAdsBannerView"), // ... };NSClassFromString()returns nil for unlinked classesNSDictionaryliteral crashes on nil valuesWhy existing fix doesn't help
PR #51838 (backported to 0.79, included in 0.81) allows disabling libraries via
react-native.config.js. But this doesn't help when:Proposed Solution
Generate nil-safe code by default:
This is backward compatible and handles all multi-target scenarios.
Alternative Solutions
RCTThirdPartyComponentsProviderfilesRelated Issues
Workaround
Currently using a post-install script to patch the generated file:
MaxAst/expo-share-extension#117 (comment)