Skip to content

Let spm deinit take an app back to CocoaPods - #59368

Open
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/spm-cocoapods-opt-out
Open

chrfalch wants to merge 1 commit into
mainfrom
chrfalch/spm-cocoapods-opt-out

Conversation

@chrfalch

@chrfalch chrfalch commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary:

SwiftPM is planned to become the default iOS setup, with CocoaPods as the opt-out. To be able to accomplish this, we want to keep the current templates with Cocoapods and do the opt-out through the npx react-native spm deinit command - meaning that the cli will:

1. Create a new app from the existing templates
2. Run the `npx react-native spm` command instead of `pod install`
3. This gives us the necessary tools for reverting/going back to Cocoapods through `npx react-native spm deinit`.

For that, an app that was converted to SwiftPM must be able to fully go back to CocoaPods. Today it cannot:

  • The conversion (spm add --deintegrate, also run by plain npx react-native spm on a new app) deletes each Podfile line that names a React Native call. A call that spans several lines, such as use_react_native!(…), loses only its first line. The Podfile is then invalid Ruby.
  • spm deinit removes SwiftPM from the Xcode project, but it does not touch the Podfile.

This PR makes npx react-native spm deinit the way back to CocoaPods:

  1. The conversion renames the Podfile. It runs pod deintegrate, then moves ios/Podfile to ios/Podfile.bak without changes. The line editing is removed.
  2. The conversion refuses in two cases, before it changes anything: the Podfile has pods that are not React Native (they would stop working under SwiftPM), or Podfile.bak already exists.
  3. spm deinit restores the Podfile from Podfile.bak and prints "Next step: run pod install". If both files exist, it stops and changes nothing.
  4. spm deinit undoes the .gitignore lines the conversion wrote. The conversion now records those lines in its marker file (.spm-injected.json), so deinit removes only them. Without a record, .gitignore is not touched.
  5. spm deinit removes the ReactNativeSPM folders that are empty after it deletes its files.

The docs in scripts/spm/__docs__ describe the way back. They also say:

  • The conversion removes the app target's own REACT_NATIVE_PATH, and deinit does not restore it. pod install sets it again for the whole project, through react_native_post_install. Only a Podfile without that call needs a manual check.
  • A SwiftPM app without a Podfile needs @react-native-community/cli 21 or newer. Older versions find the iOS project through the Podfile (see feat: detect Swift Package Manager projects react-native-community/cli#2858).

Changelog:

[IOS] [CHANGED] - react-native spm moves the Podfile to Podfile.bak when it converts an app to SwiftPM, and react-native spm deinit restores it, so an app can go back to CocoaPods.

Test Plan:

Unit tests:

yarn test packages/react-native/scripts/spm/__tests__

23 suites, 1183 tests pass. ESLint, Prettier and Flow are clean on the changed files.

A new app, outside the monorepo, on React Native 0.89.0-nightly-20261021-c7429b0e9 with CLI 21.0.0-alpha.1. The three changed script files were copied into node_modules/react-native/scripts.

  1. npx react-native spm converts the app. ios/Podfile.bak is the same as the original Podfile.
  2. SwiftPM Debug and Release builds succeed. Release makes main.jsbundle.
  3. npx react-native spm deinit restores the Podfile with the same content. It removes the .gitignore it created, the ReactNativeSPM folder and the marker. No SwiftPM entries remain in the Xcode project.
  4. cd ios && pod install succeeds.
  5. CocoaPods Debug and Release builds succeed. Release makes main.jsbundle, and REACT_NATIVE_PATH resolves to the app's node_modules/react-native.

private/helloworld in the monorepo: the same round trip passed through the CocoaPods Debug build. A second spm add --deintegrate stopped with "Nothing was changed".

Not checked: a CocoaPods Release build of helloworld. My checkout setup made Metro fail while it bundled. The same build works in the new app above.

Converting an app to SwiftPM with `--deintegrate` now moves the Podfile
to Podfile.bak instead of deleting React Native lines from it (that edit
left invalid Ruby for multi-line calls). `spm deinit` restores the
Podfile, undoes the .gitignore lines the conversion wrote, removes empty
ReactNativeSPM folders, and says to run `pod install`.

The conversion refuses, before changing anything, when the Podfile has
pods that are not React Native or when Podfile.bak already exists.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 22, 2026
@chrfalch
chrfalch requested a review from cipolleschi October 22, 2026 16:31
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 22, 2026

This branch has not been deployed

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant