diff --git a/CHANGELOG.md b/CHANGELOG.md index bad81588..4c8ff215 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- fix(react-native): Do not change the Podfile or run `pod install` when the iOS project uses Swift Package Manager ([#1386](https://github.com/getsentry/sentry-wizard/pull/1386)) + ## 9.1.0 - feat(sourcemaps): Import the Vite, webpack, Rollup and esbuild plugins from the installed SDK (`@sentry/node`, `@sentry/nestjs`, `@sentry/aws-serverless`) on v11, or install `@sentry/bundler-plugins` as a dev dependency and import from `@sentry/bundler-plugins/` ([#1382](https://github.com/getsentry/sentry-wizard/pull/1382)) diff --git a/src/react-native/react-native-wizard.ts b/src/react-native/react-native-wizard.ts index e8bb8e9c..6fd1142c 100644 --- a/src/react-native/react-native-wizard.ts +++ b/src/react-native/react-native-wizard.ts @@ -54,6 +54,7 @@ import { findDebugFilesUploadPhase, getValidExistingBuildPhases, patchBundlePhase, + usesSwiftPMAutolinking, writeXcodeProject, } from './xcode'; @@ -349,20 +350,38 @@ async function patchXcodeFiles(config: RNCliSetupConfigContent) { gitignore: false, }); - // The RNSentry pod is a Swift pod and requires `use_modular_headers!` in the - // Podfile to integrate as a static library. Patch it before `pod install` so - // the install succeeds now and any later manual `pod install` keeps working. - if (usesCocoaPod('ios')) { - traceStep('add-modular-headers', () => addModularHeaders('ios')); - } - - if (platform() === 'darwin' && (await confirmPodInstall())) { - await traceStep('pod-install', () => podInstall('ios')); - } - const xcodeProjectPath = traceStep('find-xcode-project', () => getFirstMatchedPath(XCODE_PROJECT), ); + + // `react-native spm add --deintegrate` keeps a Podfile that `pod install` + // cannot parse. + const usesSwiftPM = + !!xcodeProjectPath && usesSwiftPMAutolinking(xcodeProjectPath); + Sentry.setTag( + 'ios-dependency-manager', + usesSwiftPM ? 'swiftpm' : 'cocoapods', + ); + + if (usesSwiftPM) { + clack.log.info( + `The iOS project uses Swift Package Manager. The wizard does not change the ${chalk.cyan( + 'Podfile', + )} and does not run ${chalk.cyan('pod install')}.`, + ); + } else { + // The RNSentry pod is a Swift pod and requires `use_modular_headers!` in the + // Podfile to integrate as a static library. Patch it before `pod install` so + // the install succeeds now and any later manual `pod install` keeps working. + if (usesCocoaPod('ios')) { + traceStep('add-modular-headers', () => addModularHeaders('ios')); + } + + if (platform() === 'darwin' && (await confirmPodInstall())) { + await traceStep('pod-install', () => podInstall('ios')); + } + } + Sentry.setTag( 'xcode-project-status', xcodeProjectPath ? 'found' : 'not-found', diff --git a/src/react-native/xcode.ts b/src/react-native/xcode.ts index 598d663b..a4b7f8b3 100644 --- a/src/react-native/xcode.ts +++ b/src/react-native/xcode.ts @@ -196,6 +196,17 @@ export function findDebugFilesUploadPhase( }); } +// React Native SwiftPM autolinking adds a local package at this path. +export function usesSwiftPMAutolinking(xcodeProjectPath: string): boolean { + try { + return /relativePath = "?build\/generated\/autolinking"?;/.test( + fs.readFileSync(xcodeProjectPath, 'utf-8'), + ); + } catch { + return false; + } +} + export function writeXcodeProject( xcodeProjectPath: string, xcodeProject: Project, diff --git a/test/react-native/xcode.test.ts b/test/react-native/xcode.test.ts index 40768395..77a1a0b0 100644 --- a/test/react-native/xcode.test.ts +++ b/test/react-native/xcode.test.ts @@ -6,7 +6,11 @@ import { findBundlePhase, findDebugFilesUploadPhase, ErrorPatchSnippet, + usesSwiftPMAutolinking, } from '../../src/react-native/xcode'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; import chalk from 'chalk'; import { makeCodeSnippet } from '../../src/utils/clack'; // @ts-expect-error - clack is ESM and TS complains about that. It works though @@ -435,4 +439,36 @@ sentry-cli upload-dsym path/to/dsym --include-sources expect(findDebugFilesUploadPhase(input)).toBeUndefined(); }); }); + + describe('usesSwiftPMAutolinking', () => { + const writeProject = (content: string) => { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-xcode-project')); + const projectPath = path.join(dir, 'project.pbxproj'); + fs.writeFileSync(projectPath, content); + return projectPath; + }; + + it('returns true for a project with the autolinking package', () => { + const projectPath = + writeProject(`/* Begin XCLocalSwiftPackageReference section */ + C2FECF1430FDAAE3BF355839 /* XCLocalSwiftPackageReference "build/generated/autolinking" */ = {isa = XCLocalSwiftPackageReference; relativePath = build/generated/autolinking;}; +/* End XCLocalSwiftPackageReference section */`); + + expect(usesSwiftPMAutolinking(projectPath)).toBe(true); + }); + + it('returns false for a CocoaPods project', () => { + const projectPath = writeProject( + `shellScript = "\"\${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n";`, + ); + + expect(usesSwiftPMAutolinking(projectPath)).toBe(false); + }); + + it('returns false if the project file does not exist', () => { + expect( + usesSwiftPMAutolinking(path.join(os.tmpdir(), 'missing.pbxproj')), + ).toBe(false); + }); + }); });