Skip to content

RCTThirdPartyComponentsProvider crashes on multi-target iOS apps (ShareExtension, App Clip) #56469

@poteboy

Description

@poteboy

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

  1. Codegen generates a single RCTThirdPartyComponentsProvider.mm shared between all targets
  2. It uses NSDictionary literal syntax:
    thirdPartyComponents = @{
        @"RNGoogleSignInButton": NSClassFromString(@"RNGoogleSignInButtonComponentView"),
        @"RNGoogleMobileAdsBannerView": NSClassFromString(@"RNGoogleMobileAdsBannerView"),
        // ...
    };
  3. ShareExtension/App Clip targets don't link all libraries (size constraints, API limitations)
  4. NSClassFromString() returns nil for unlinked classes
  5. 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

  1. Generate target-specific RCTThirdPartyComponentsProvider files
  2. 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: Author FeedbackNeeds: ReproThis issue could be improved with a clear list of steps to reproduce the issue.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions