Repository navigation
Conversation
|
@krozniata regarding nitro, can you reach out to @mrousavy on slack to see what's the plan? |
|
I'm already actively working on it :) |
# Conflicts:
# packages/create-react-native-library/templates/objc-view-new/ios/{%- project.name %}View.mm
| // Must match the name React Native's SPM autolinker derives from the package name | ||
| const spmName = project.slug | ||
| .replace(/^@[^/]+\//, '') | ||
| .split(/[^a-zA-Z0-9]+/) | ||
| .filter(Boolean) | ||
| .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) | ||
| .join(''); |
There was a problem hiding this comment.
Via Claude: This name doesn't match what React Native 0.88 expects. The SwiftPM name now comes from swiftpmConfig.name, then the podspec name, and only then the npm name (docs). Our podspec uses project.name, so for an unscoped package like react-native-awesome-module, the app looks for AwesomeModule while we declare ReactNativeAwesomeModule. It fails in both the example app and a standalone 0.88 app with the packed library:
product 'AwesomeModule' required by package 'autolinking' target 'AutolinkedAggregate' not found in package 'AwesomeModule'.
We can use project.name here and add "swiftpmConfig": { "name": "<%- project.name -%>" } to package.json. With that, both apps build and run. Then we don't need to compute the name in the template either.
| rm -rf ${{ env.work_dir }} | ||
|
|
||
| ./packages/create-react-native-library/bin/create-react-native-library ${{ env.work_dir }} \ | ||
| --slug @bob/react-native-test \ |
There was a problem hiding this comment.
Via Claude: With a scoped slug, both naming schemes give ReactNativeTest, so this job passes even though unscoped libraries fail to build (see the comment on Package.swift). Let's use an unscoped slug here so the job catches name mismatches.
| `Set up ${kleur.bold('Swift Package Manager')} for the example app (React Native 0.87+)` | ||
| )}${kleur.gray(':')} | ||
|
|
||
| ${kleur.gray('$')} cd example && npx react-native spm --deintegrate` |
There was a problem hiding this comment.
Via Claude: After cd example, the next step (yarn example ios) fails with Couldn't find a script named "example". We can run it through the workspace script instead, so the user stays in the root:
yarn example react-native spm --deintegrate| project: { | ||
| ios: { | ||
| automaticPodsInstallation: true, | ||
| automaticPodsInstallation: <%- experimentalSpm ? 'false' : 'true' %>, |
There was a problem hiding this comment.
Via Claude: Once the example is moved to SwiftPM, the generated CI and turbo config still assume CocoaPods:
ci.ymlrunspod install, but after--deintegratethePodfiledoesn't parse anymore (React Native 0.88.0 removesuse_react_native!(and leaves its arguments behind).- A fresh checkout needs
npx react-native spmbefore building, since everything it generates is gitignored (docs). React Native suggests apostinstallscript for this. turbo.jsonlists*.podspecin thebuild:iosinputs but notPackage.swift, so changes to the manifest don't invalidate the cache.
The CI and turbo templates need to handle the SwiftPM case too.
| npx create-react-native-library@latest awesome-library --experimental-spm | ||
| ``` | ||
|
|
||
| The podspec is still generated, so CocoaPods keeps working. The example app is set up for SPM instead of CocoaPods: run `npx react-native spm --deintegrate` in `example` once to set it up, and `npx react-native spm` after a fresh install. |
There was a problem hiding this comment.
Via Claude: The generated CONTRIBUTING.md still describes the CocoaPods setup. It says to open the .xcworkspace and find the sources under Pods > Development Pods, and SwiftPM creates neither. It needs SwiftPM instructions too, including running npx react-native spm after a fresh clone.
| cxxSettings: [ | ||
| <% if (project.cpp) { -%> | ||
| .headerSearchPath("cpp"), | ||
| .headerSearchPath("ios/generated/ReactCodegen"), |
There was a problem hiding this comment.
Via Claude: ios/generated/ReactCodegen doesn't exist since #819, and the spec header comes from the app's ReactAppHeaders now. The C++ example builds without it, so we can remove this line.
| experimentalSpm: { | ||
| type: 'confirm', | ||
| description: 'Whether to generate an experimental Swift package manifest', | ||
| message: 'Generate a Swift Package Manager manifest?', | ||
| default: false, | ||
| skip: () => !process.argv.includes('--experimental-spm'), |
There was a problem hiding this comment.
Via Claude: Let's use skip: true like reactNativeVersion, so it's only set from the CLI and never prompted. It should also go last, or at least before reactNativeVersion, instead of first.
| } else if (answers.experimentalSpm === true && config.project.native) { | ||
| console.warn( | ||
| "Swift Package Manager manifest was not generated: Nitro libraries mix Swift and C++ sources, which Swift Package Manager can't compile in a single target." | ||
| ); |
There was a problem hiding this comment.
Via Claude: The other warnings (in index.ts and generateExampleApp.ts) have a kleur.yellow('⚠') prefix and are printed from there, not from applyTemplates. This also repeats the condition from generateTemplateConfiguration, and JS-only libraries get no warning. Let's do the check and print the warning once, next to the --react-native-version warning in index.ts.
| ? ` | ||
|
|
||
| ${kleur.magenta( | ||
| `Set up ${kleur.bold('Swift Package Manager')} for the example app (React Native 0.87+)` |
There was a problem hiding this comment.
Via Claude: We mix "SPM", "SwiftPM" and "Swift Package Manager" across the docs, CLI output and comments. React Native uses "Swift Package Manager (SwiftPM)" on first mention and "SwiftPM" after that, and keeps spm for the command. Let's use the same terms. This should also say 0.88+, since the name fix relies on swiftpmConfig, which 0.87 doesn't read.
| To also generate a `Package.swift` for React Native experimental [Swift Package Manager support](https://reactnative.dev/blog/2026/08/11/react-native-0.87#experimental-swift-package-manager-support-for-ios), pass `--experimental-spm`: | ||
|
|
||
| ```sh | ||
| npx create-react-native-library@latest awesome-library --experimental-spm | ||
| ``` | ||
|
|
||
| The podspec is still generated, so CocoaPods keeps working. The example app is set up for SPM instead of CocoaPods: run `npx react-native spm --deintegrate` in `example` once to set it up, and `npx react-native spm` after a fresh install. | ||
|
|
||
| ### Limitations | ||
|
|
||
| - Requires React Native 0.87 or newer | ||
| - Nitro libraries aren't supported, since SwiftPM can't compile Swift and C++ sources in the same target. | ||
| - Expo and react-native-test-app examples can't use SPM yet. |
There was a problem hiding this comment.
Via Claude: This splits the usage section, so "This will ask you a few questions..." now reads like it's about the SwiftPM command. Let's move it to its own section, like "Local library".
| - name: Build example (iOS, Debug) | ||
| working-directory: ${{ env.work_dir }} | ||
| run: | | ||
| yarn example build:ios |
There was a problem hiding this comment.
Via Claude: This job also bypasses the existing turbo setup. It builds with yarn example build:ios and has no turbo cache, so every run does a full build. Moving it to yarn turbo run build:ios needs two more changes:
Package.swiftisn't in thebuild:iosinputs in theturbo.jsontemplate, so editing it keeps the same hash and turbo reuses the cached build.npx react-native spmrewrites tracked files underexample/ios(project.pbxproj, the scheme,Podfile,.spm-injected.json), so the hash changes after setup. The cache check needs to run after setup, or it won't match what the build saved.
| run: | | ||
| # The CLI builds Release for a device, which requires code signing | ||
| project=$(ls -d *.xcodeproj) | ||
| xcodebuild -project "$project" -scheme "${project%.xcodeproj}" -configuration Release -sdk iphonesimulator -destination "generic/platform=iOS Simulator" build |
There was a problem hiding this comment.
Via Claude: This step skips turbo completely. The generated turbo.json only has a Debug build:ios task, so there's nothing for a Release build to go through, and every run does two uncached Xcode builds. If we want to keep the Release build, the example needs a Release script and turbo.json needs a task for it.
|
|
||
| ${kleur.gray('$')} yarn | ||
| ${kleur.gray('$')} yarn${ | ||
| config.experimentalSpm && config.example === 'vanilla' |
There was a problem hiding this comment.
Via Claude: With --example expo or --example test-app, the library still gets a Package.swift, but the example stays on CocoaPods and nothing tells the user. So the manifest gets published without being built anywhere. Let's warn about this the same way as for Nitro, or skip the manifest for these examples.
|
|
||
| ### Limitations | ||
|
|
||
| - Requires React Native 0.87 or newer |
There was a problem hiding this comment.
Via Claude: This should say 0.88, since the name fix relies on swiftpmConfig in package.json, which 0.87 doesn't read.
| 'example', | ||
| 'reactNativeVersion', | ||
| 'local', | ||
| 'experimentalSpm', |
There was a problem hiding this comment.
Via Claude: This looks fine. The list is for answers we can derive from the generated package.json, and we can tell this one from Package.swift in files (and from swiftpmConfig after the name fix). Nothing in the repo reads this metadata either.
| *.tsbuildinfo | ||
|
|
||
| # Swift Package Manager | ||
| .build/ |
There was a problem hiding this comment.
Via Claude: This is needed. A .build/ folder showed up at the repo root while working on this branch, even though nothing ran swift build there. Its marker file says SwiftPM's build system wrote it. The exact trigger isn't clear, but it's likely editor tooling picking up templates/spm/Package.swift.
|
JFYI Nitro V1 is now on SPM. It dropped CocoaPods support. |
Summary
This PR adds an experimental
--experimental-spmflag that generates a Package.swift for Turbo Modules and Fabric views, targeting React Native 0.87's Swift Package Manager supportWhen passing flag it adds
Package.swiftfile to library to support SPM and also disables automatic pod installation in example appAlso adds CI workflows that create such library and build app that uses SPM package ('npx react-native spm --deintegrate`)
Test plan
npx react-native spm --deintegrateAlso CI GREEN