Repository navigation
Conversation
|
|
hknakn
marked this pull request as ready for review
October 21, 2026 09:05
3 of 4 tasks
chrfalch
added a commit
to expo/expo
that referenced
this pull request
Oct 22, 2026
…modules (#52017) # Why Fixes #51652. With `useFrameworks: "dynamic"` and precompiled modules on, `pod install` fails for apps that use `@stripe/stripe-react-native` 0.81.0. Expo builds every pod that depends on React-Core as a static library. Stripe gets its iOS SDK through React Native's `spm_dependency`, and its own post-install check stops the install unless its pod is a dynamic framework. Stripe's error message tells the user to turn on dynamic frameworks, which they already did. Today the only workaround is to turn off precompiled modules. # How Expo now records which pods call React Native's `spm_dependency`. With dynamic linkage, it keeps those pods and all their dependencies out of the static downgrade. - **Recording.** React Native's `SPMManager` has no public reader for these pods. `use_expo_modules!` prepends a small module on `SPMManager#dependency`, which records the root pod name and calls `super`. It does nothing if `SPMManager` is not loaded, and it does not read React Native's private state. - **Whole dependency graph.** CocoaPods rejects a dynamic pod with any static recursive dependency (`verify_no_static_framework_transitive_dependencies`). So the pod and every one of its `recursive_dependent_targets` keep their linkage. Keeping only the unbuilt dependencies failed validation on `ReactCodegen`. - **Core pods still win.** `framework_modules_to_patch` (ExpoModulesCore, ExpoModulesJSI, Expo, ReactAppDependencyProvider, expo-dev-menu, plus `forceStaticLinking`) is forced static again later in `installer.rb`. If an `spm_dependency` pod's graph contains one of these, Expo keeps today's result for that pod and prints which pod blocks it. Such pods work today, because React Native's SPM support handles static pods, so this avoids a regression. - **Scope.** This applies only with `:dynamic` linkage. With no `spm_dependency` pods, or with static linkage, the downgrade set is the same as before. - **Refactor for testing.** The downgrade decision moved into `pods_to_downgrade(pod_targets, linkage:, framework_modules_to_patch:, spm_dependency_pods:)`, so it can be tested with fake targets. - **@react-native-firebase compatibility (second commit).** `@react-native-firebase` 27.0.0 is also SPM-only and calls `spm_dependency`. It has its own post-install workaround for precompiled modules (`rnfirebase_restore_dynamic_linkage_after_expo_prebuilt!`). That workaround calls `Pod::Target#build_type` on every RNFB pod, and CocoaPods keeps that method private. It only worked because Expo's downgrade defined a public `build_type` on each downgraded pod. With the first commit alone, RNFB pods stay dynamic and `pod install` stops with `NoMethodError: private method 'build_type'`. That would break Firebase apps that install today. Expo now gives every pod target that it does not downgrade a public `build_type` that returns the real value through `super`. RNFB's workaround then sees its pods as dynamic and does nothing. Stripe has a related open PR, stripe/stripe-react-native#2757. It lets Stripe accept a static pod, but it also needs react/react-native#59182, which is not in RN 0.88.0. Stripe still accepts a dynamic pod, so the two changes do not conflict. In the Stripe app, the downgrade list goes from 85 pods to 25. `ReactCodegen` is now a dynamic framework in that app. Stripe's SPM products link statically into `stripe_react_native.framework`. # Test Plan **Unit tests.** New file: `e2e/__tests__/precompiled-pods-to-downgrade-test.ts`, 10 tests. It runs the real `precompiled_modules.rb` in Ruby with fake pod targets. Before the first commit, 8 of 9 failed. The `build_type` test failed before the second commit with the same private-method error that RNFB hits. All 10 pass on Ruby 3.3.6 and on the system Ruby 2.6.10. The reviewer also removed parts of the fix on purpose: removing the core-pod check makes 2 tests fail, removing the dynamic-only check makes 1 fail, and removing the public `build_type` makes 1 fail. ``` jest --no-watchman e2e/__tests__/precompiled-pods-to-downgrade-test.ts Tests: 10 passed, 10 total ``` **Repro app.** The app comes from https://github.com/glowcore/expo-58-stripe-spm-repro, with `expo-updates` added. Versions: SDK 58, RN 0.88.0, Stripe 0.81.0, CocoaPods 1.16.2, Xcode 27.1. The fix was applied to the app's `node_modules`. | Check | Without fix | With fix | |---|---|---| | `pod install` | fails: "requires dynamic frameworks, but stripe-react-native is building as a static library" | passes, `Keeping USE_FRAMEWORKS for ... (stripe-react-native)` | | Debug simulator build | – | succeeds | | Release simulator build (earlier version of the change) | – | succeeds | | Debug device build (`CODE_SIGNING_ALLOWED=NO`) | – | succeeds | | Release archive (`CODE_SIGNING_ALLOWED=NO`) | – | succeeds. `stripe_react_native.framework` is dynamic, and `otool -L` shows only `@rpath`/system paths | | Launch on the iOS simulator | – | renders. `StripeSdk` is present, `initialise` resolves, `CardField` is registered. No dyld errors and no duplicate classes | The build, archive and launch rows ran before the second commit. With the second commit, `pod install` passes again in this app. **Second app: Stripe + Firebase + Sentry.** The same app with `@react-native-firebase/app` and `/messaging` 27.0.0 (published code, unchanged) and `@sentry/react-native` 8.28.0. | Check | Result | |---|---| | `pod install`, no fix | fails: Stripe's dynamic-framework error | | `pod install`, first commit only | fails: RNFB `NoMethodError: private method 'build_type'` | | `pod install`, both commits | passes, `Keeping USE_FRAMEWORKS for ... (RNFBApp, RNFBMessaging, stripe-react-native)`. `RNSentry` stays static, as before | | Debug simulator build, both commits (earlier form of the second commit) | succeeds | | `pod install` with precompiled modules off | passes | **Not tested:** running a Release build, running on a physical device, launching the Firebase app, and CocoaPods 1.17.0. **Local package suite.** 18 tests in `e2e/monorepo-test.ts` fail in my worktree, because they need the package's `build/`, which is not there. The change touches only Ruby and adds a test file. # Checklist - [x] I added a `changeset` (`.changeset/autolinking-keep-spm-dependency-pods-dynamic.md`). - [ ] `et check-packages` passes. Not run yet; see the note above. - [x] This diff works with `npx expo prebuild` (tested in the repro app). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md).
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Support static Stripe SPM consumers with React Native's shared build products.
Problem
Expo precompiled modules make Stripe's adapter static. Stripe rejects this configuration even when React Native supports it.
Solution
Accept static libraries configured with shared build products. Preserve dynamic-framework support and reject unsupported configurations.
Motivation
Expo SDK 58 precompiled modules make React-Core consumers static, including
stripe-react-native. Stripe currently rejects that target before compilation, even when React Native has configured static SPM support. This prevents consumers from enabling Expo precompiled modules.Related report: expo/expo#51652
Static support also requires react/react-native#59182. React Native currently reconstructs module-map paths from the pod name rather than the module name; Stripe's pod and module names differ. Keep this dependency in mind before merging or releasing static support.
The check uses the generated native target's build settings instead of assuming that a React Native version includes the required support. Every configuration must use
${PODS_CONFIGURATION_BUILD_DIR}.Testing
I tested this manually
I added automated tests
ruby scripts/test/stripe_spm_test.rb: seven tests, 17 assertions, no failures. The static-library case failed before the change.Fresh Expo 58.0.11 / React Native 0.88.0 reproduction with
useFrameworks: "dynamic"andusePrecompiledModules: true: the original Stripe 0.81.0 package fails pod installation with the dynamic-linkage error; the candidate package from this branch completes pod installation.The Stripe candidate alone completes pod installation but exposes React Native's module-map path bug during compilation. With the existing React Native PR's helper at
6569aeb9ca9735aed3a008a9f28703a8caa64cadloaded in the isolated reproduction, the ARM64 iOS Simulator Debug build succeeds with precompiled modules enabled.ruby spm-test.rbin the React Native PR: nine tests, 25 assertions, no failures.On an iPhone 18 Pro Max simulator,
initStriperesolves successfully and the native CardField renders its number, expiry and CVC fields. Entering4242424242424242displays Visa. Complete card entry, Release archives and payment transactions have not been tested.Documentation
CONTRIBUTING.mddescribes supported linkage and the requiredreact_native_post_installcall.Testing directions
ruby scripts/test/stripe_spm_test.rb. Build an Expo SDK 58 app withusePrecompiledModules: trueand the React Native prerequisite; verify initialization and native CardField rendering.