Skip to content

Commit dda80a2

Browse files
committed
Warn instead of refusing --deintegrate with other pods
The Podfile is moved to Podfile.bak, so no pod is lost and `spm deinit` restores it. An explicit `--deintegrate` now lists the other pods, says CocoaPods will not install them while the app uses SwiftPM unless SwiftPM provides them, and continues. RNTester needs this: its Podfile has local example pods that SwiftPM provides. The automatic conversion of a new app still skips such Podfiles.
1 parent 73e2c1d commit dda80a2

4 files changed

Lines changed: 152 additions & 49 deletions

File tree

‎packages/react-native/scripts/setup-apple-spm.js‎

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -750,18 +750,13 @@ function pbxprojUsesCocoaPods(xcodeprojPath /*: string */) /*: boolean */ {
750750
}
751751
}
752752

753-
// A web link, because npm does not ship the __docs__ directories.
754-
const KEEPING_NON_RN_PODS_DOCS_URL =
755-
'https://github.com/facebook/react-native/blob/main/packages/react-native/' +
756-
'scripts/spm/__docs__/spm-scripts.md#keeping-non-rn-pods';
757-
758753
function cocoaPodsIntegratedMessage(xcodeprojPath /*: string */) /*: string */ {
759754
return (
760-
`${path.basename(xcodeprojPath)} is CocoaPods-integrated. If your ` +
761-
'Podfile declares only React Native, re-run `spm add --deintegrate` ' +
762-
'(runs `pod deintegrate` and moves the Podfile to Podfile.bak). If it ' +
763-
'declares other pods, convert by hand as described in "Keeping non-RN ' +
764-
`pods": ${KEEPING_NON_RN_PODS_DOCS_URL}`
755+
`${path.basename(xcodeprojPath)} is CocoaPods-integrated. Re-run ` +
756+
'`spm add --deintegrate`. It runs `pod deintegrate` and moves the Podfile ' +
757+
'to Podfile.bak. Pods other than React Native are not installed while ' +
758+
'the app uses SwiftPM, unless SwiftPM provides them. `spm deinit` ' +
759+
'restores the Podfile.'
765760
);
766761
}
767762

@@ -778,16 +773,29 @@ function podfileHasRnIntegration(appRoot /*: string */) /*: boolean */ {
778773
);
779774
}
780775

781-
// True when the Podfile declares any explicit `pod '...'` or `pod('...')`
782-
// (third-party pods), at the start of a line or after a `;`.
783-
function podfileHasThirdPartyPods(appRoot /*: string */) /*: boolean */ {
776+
// The names of the pods the Podfile declares explicitly (`pod '...'` or
777+
// `pod('...')`), deduplicated, in order. React Native itself comes in through
778+
// use_react_native!, so every name here is a pod that is not part of it.
779+
function podfilePodNames(appRoot /*: string */) /*: Array<string> */ {
784780
const podfilePath = path.join(appRoot, 'Podfile');
785781
if (!fs.existsSync(podfilePath)) {
786-
return false;
782+
return [];
783+
}
784+
const names /*: Set<string> */ = new Set();
785+
for (const line of fs.readFileSync(podfilePath, 'utf8').split('\n')) {
786+
// `#{` is Ruby string interpolation, not a comment.
787+
for (const statement of line.split(/#(?!\{)/)[0].split(';')) {
788+
const match = /^\s*pod\s*\(?\s*(['"])(.+?)\1/.exec(statement);
789+
if (match != null) {
790+
names.add(match[2]);
791+
}
792+
}
787793
}
788-
return /^(?:[^#\n]*;)?\s*pod\s*\(?\s*['"]/m.test(
789-
fs.readFileSync(podfilePath, 'utf8'),
790-
);
794+
return [...names];
795+
}
796+
797+
function podfileHasThirdPartyPods(appRoot /*: string */) /*: boolean */ {
798+
return podfilePodNames(appRoot).length > 0;
791799
}
792800

793801
// The zero-arg "fresh project" safe-gate: auto-`add --deintegrate` ONLY when it
@@ -1296,19 +1304,22 @@ async function main(argv /*:: ?: Array<string> */) /*: Promise<void> */ {
12961304
return;
12971305
}
12981306

1299-
if (args.deintegrate && podfileHasThirdPartyPods(appRoot)) {
1300-
logError(
1301-
'Your Podfile declares pods that are not part of React Native. They ' +
1302-
'would stop working under SwiftPM, so the conversion is refused. ' +
1303-
'Nothing was changed.',
1304-
);
1305-
process.exitCode = 1;
1306-
return;
1307-
}
13081307
if (args.deintegrate && refuseToOverwritePodfileBackup(appRoot)) {
13091308
process.exitCode = 1;
13101309
return;
13111310
}
1311+
if (args.deintegrate) {
1312+
const podNames = podfilePodNames(appRoot);
1313+
if (podNames.length > 0) {
1314+
logError(
1315+
'Your Podfile declares pods that are not part of React Native: ' +
1316+
`${podNames.join(', ')}. CocoaPods will not install them while ` +
1317+
'the app uses SwiftPM, unless SwiftPM provides them. They are kept ' +
1318+
'in Podfile.bak, and `npx react-native spm deinit` restores the ' +
1319+
'Podfile.',
1320+
);
1321+
}
1322+
}
13121323

13131324
// Fail fast on a CocoaPods-integrated target BEFORE the (expensive) pipeline,
13141325
// so the user isn't made to wait through codegen + artifact download only to
@@ -1581,6 +1592,7 @@ module.exports = {
15811592
findInjectedXcodeproj,
15821593
generateAutolinkingConfigOrFailClosed,
15831594
parseArgs,
1595+
podfilePodNames,
15841596
resolveAction,
15851597
resolveAppIosDeploymentTarget,
15861598
resolveConfigCommandToPin,

‎packages/react-native/scripts/spm/__docs__/README.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ npx react-native spm # add on first run, update thereafter
2525

2626
On a fresh CocoaPods app, this converts the app to SwiftPM. It runs
2727
`pod deintegrate` and moves the `Podfile` to `Podfile.bak`. A Podfile with pods
28-
that are not part of React Native is refused. Without a Podfile, the CLI needs
29-
`@react-native-community/cli` 21 or newer to find the iOS project.
28+
that are not part of React Native is not converted automatically;
29+
`spm add --deintegrate` converts it and warns that those pods are not installed.
30+
Without a Podfile, the CLI needs `@react-native-community/cli` 21 or newer to
31+
find the iOS project.
3032

3133
To go back to CocoaPods, run `npx react-native spm deinit`, then `pod install`
3234
in `ios/`. If your Podfile does not call `react_native_post_install`, check

‎packages/react-native/scripts/spm/__docs__/spm-scripts.md‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ The zero-arg conversion of a fresh app does the same steps. It does not start
5252
when `Podfile.bak` exists; the command then fails as a plain `add` does.
5353

5454
If the Podfile declares pods that are not part of React Native, `--deintegrate`
55-
refuses and changes nothing. Those pods would stop working under SwiftPM. See
55+
converts the app anyway and names those pods in a warning. See
5656
[Keeping non-RN pods](#keeping-non-rn-pods).
5757

5858
After the conversion, the app has no Podfile. The CLI then finds the iOS project
@@ -62,8 +62,13 @@ the iOS project.
6262

6363
### Keeping non-RN pods
6464

65-
`--deintegrate` does not convert an app whose Podfile has pods that are not part
66-
of React Native. To keep those pods side-by-side, convert by hand:
65+
`--deintegrate` converts an app whose Podfile has pods that are not part of
66+
React Native, and warns. While the app uses SwiftPM, CocoaPods does not install
67+
those pods, unless SwiftPM provides them as an autolinked library or a
68+
[local native module](#local-native-modules). The pods stay in `Podfile.bak`,
69+
and `spm deinit` restores the Podfile.
70+
71+
To keep a CocoaPods-only pod next to SwiftPM, convert by hand:
6772

6873
```bash
6974
pod deintegrate # removes the CocoaPods integration from the .xcodeproj
@@ -140,7 +145,7 @@ Flags below use the `react-native spm` (camelCase) form.
140145
| `--yes` | Skip the dirty-pbxproj confirmation prompt |
141146
| `--xcodeproj <path>` | [add, update, scaffold, deinit] Which `.xcodeproj` to inject into or remove from (when several exist) |
142147
| `--productName <name>` | [add, update, scaffold] Which app target to inject into (when several exist) |
143-
| `--deintegrate` | [add] Run `pod deintegrate` and move the Podfile to `Podfile.bak` before injecting. Refused if the Podfile has pods that are not part of React Native |
148+
| `--deintegrate` | [add] Run `pod deintegrate` and move the Podfile to `Podfile.bak` before injecting. Warns about pods that are not part of React Native; see [Keeping non-RN pods](#keeping-non-rn-pods) |
144149
| `--artifacts <path>` | [advanced] Local artifact root containing complete `debug/` and `release/` cache slots |
145150
| `--download <auto\|skip\|force>` | [advanced] Artifact download policy (default: auto) |
146151
| `--skipCodegen` | [advanced] Skip the codegen step |
@@ -302,7 +307,7 @@ Paths are relative to the Xcode project directory (`ios/`) unless noted.
302307
| `ReactNativeSPM/Scripts/` | `add`, `update`, `scaffold` | Regenerated on each run and written when their content changes: `sync-autolinking.sh` and `embed-flavored-frameworks.sh`, the scripts the Sync and embed phases run. Plugin script phases keep their scripts in `project.pbxproj`. **Commit it.** When a script is missing, its phase fails the build with `error: <path> is missing. Run 'npx react-native spm update' to generate it.` | Yes — both scripts are deleted, then each `ReactNativeSPM/` directory left empty |
303308
| `MyApp.xcodeproj/xcshareddata/xcschemes/*.xcscheme` | `add`, `update`, `scaffold` | The sync pre-action is added to the scheme that builds your target; a shared scheme is created if there is none. Commit this or teammates lose the pre-action. | Yes — a scheme `add` created is deleted only if it is still byte-identical to the generated one; otherwise only the pre-action is stripped |
304309
| `.gitignore` | `add` only | Created if absent, else appended: a `# SPM – auto-generated at build time` block adding `Package.resolved`, `build/generated/`, `build/xcframeworks/`, `.build/`. | Yes — only the lines `add` wrote, as recorded in the marker, and the file too if `add` created it and nothing else is left. A line you changed is kept, and `deinit` says so. A marker from an older version has no record, so `.gitignore` is left as it is |
305-
| `Podfile` | `add --deintegrate` | Moved to `Podfile.bak`, unchanged. Refused, before any change, if the Podfile has pods that are not part of React Native or `Podfile.bak` already exists. | Yes — `Podfile.bak` is moved back to `Podfile`. If both files exist, `deinit` stops before it changes anything |
310+
| `Podfile` | `add --deintegrate` | Moved to `Podfile.bak`, unchanged. Refused, before any change, if `Podfile.bak` already exists. | Yes — `Podfile.bak` is moved back to `Podfile`. If both files exist, `deinit` stops before it changes anything |
306311
| `Pods/`, `Pods-*.xcconfig`, `[CP]` phases | `add --deintegrate` | Removed by `pod deintegrate`. The `.xcworkspace` referencing them is left on disk. | **No** — run `pod install` to restore |
307312

308313
The two pins are the `--version` pin (`artifactsVersionOverride`, see
@@ -730,7 +735,7 @@ across apps; refresh it with `react-native spm update --download force`.
730735
| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
731736
| `xcodebuild` fails: "Could not resolve package dependencies … `build/generated/autolinking` doesn't exist" | Fresh clone — run `npx react-native spm` once before building (see [Fresh clones & CI](#fresh-clones--ci)) |
732737
| Build fails: `error: …/ReactNativeSPM/Scripts/<name>.sh is missing` | `ReactNativeSPM/` was not committed. Run `npx react-native spm update`, then commit `ReactNativeSPM/` |
733-
| `spm add` fails: "CocoaPods-integrated project" | Re-run `spm add --deintegrate` (runs `pod deintegrate` and moves the Podfile to `Podfile.bak`). If the Podfile has pods that are not part of React Native, convert by hand (see [Keeping non-RN pods](#keeping-non-rn-pods)). |
738+
| `spm add` fails: "CocoaPods-integrated project" | Re-run `spm add --deintegrate` (runs `pod deintegrate` and moves the Podfile to `Podfile.bak`). Pods that are not part of React Native are not installed while the app uses SwiftPM (see [Keeping non-RN pods](#keeping-non-rn-pods)). |
734739
| `spm add` fails: "no .xcodeproj found" | Create an app first (`npx @react-native-community/cli init`) or make a project in Xcode, then `spm add`. |
735740
| `spm add` fails: "multiple .xcodeproj found" | Pass `--xcodeproj <path>` (and `--productName <target>` if multiple app targets). |
736741
| `Package.swift is missing for library "<name>"` (exit 2) | The dep ships no SwiftPM support. `npx react-native spm scaffold`, then re-run setup; persist with `patch-package`. See [Community packages without a Package.swift](#community-packages-without-a-packageswift) |

0 commit comments

Comments
 (0)