Skip to content

[iOS] PaywallViewManager class name doesn't match RCT_EXPORT_MODULE(Paywall), aborting on Fabric interop assertion #1886

Description

@alex-pominov

Summary

ios/PaywallViewManager.m exports module Paywall but names its class PaywallViewManager. React Native's Fabric legacy-view-manager interop resolves managers by name, so the lookup fails and the process aborts on an assertion during a teardown/reload race.

Paywall is the only manager in the package with this mismatch — both siblings are already consistent, which suggests an oversight rather than intent.

The crash

SIGABRT: Assertion failed: (viewManagerClass),
function constructCoordinator,
file LegacyViewManagerInteropComponentDescriptor.mm, line 125

Mechanism

getViewManagerFromComponentName (LegacyViewManagerInteropComponentDescriptor.mm) derives the class name from the component name by appending "Manager", then tries RCT<name> followed by <name>.

For module Paywall it therefore looks for RCTPaywallManager and PaywallManager. The declared class is PaywallViewManager — the View infix is not present in the module name — so both lookups return nil.

RN then falls back to [bridge moduleForName:@"Paywall"]. That usually succeeds, which is why this isn't seen constantly; but during a teardown/reload race the bridge returns nil, and assert(viewManagerClass) aborts the process.

It's the sole outlier in this package

RCT_EXPORT_MODULE Class Resolves?
CustomerCenterView CustomerCenterViewManager
RCPaywallFooterView RCPaywallFooterViewManager
Paywall PaywallViewManager

Conditions

  • New Architecture (Fabric) with legacy view-manager interop
  • A teardown/reload race — Expo dev-client reloads make this substantially more likely

Probably why it has gone unreported: the assertion names React Native, not RevenueCat, so it reads as an RN bug.

Fix

Either direction works; the first matches your own siblings:

-@interface PaywallViewManager : RCTViewManager
+@interface PaywallManager : RCTViewManager

(and the matching @interface PaywallManager () / @implementation PaywallManager in the .m)

or alternatively:

-RCT_EXPORT_MODULE(Paywall)
+RCT_EXPORT_MODULE(PaywallView)

The rename is the safer of the two — it keeps the JS-facing module name Paywall unchanged.

Version

Verified against 10.5.0 (current at time of writing): ios/PaywallViewManager.h still declares @interface PaywallViewManager : RCTViewManager and ios/PaywallViewManager.m still has RCT_EXPORT_MODULE(Paywall). The file is byte-identical to 10.4.4.

We carry this as a local patch and have had to re-port it on each version bump; we'd much rather drop it. Happy to open a PR if that's useful — just say which of the two fixes you'd prefer.

Environment

  • react-native-purchases-ui 10.5.0
  • React Native 0.86, New Architecture / Fabric enabled
  • Expo SDK 57, iOS

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions