Skip to content

Commit cf6a627

Browse files
chrfalchclaude
andcommitted
Derive an SPM library's Swift name from its podspec, not its npm name
An autolinked library's SwiftPM target name is also its header import prefix, so deriving it from the npm package name was wrong for most of the ecosystem (react-native-svg publishes RNSVG, not ReactNativeSvg) and wrong silently — no error, just headers nobody can import under the expected name. The podspec already carries the truth and is already parsed, so resolution now reads it: spm.name, then podspec header_dir, then the podspec name, then toSwiftName(npm name) for a library that ships no podspec. A prefix Swift cannot spell is normalized to the identifier SwiftPM would compile it as, with a warning, rather than silently reverting to the npm name. A reserved name or two deps landing on one name is a hard error naming spm.name as the fix; the scope-borrowing that auto-corrected collisions is removed, since a name the build invents is a name no #import can predict. Collision checks key on SwiftPM's c99 name, so react-native-svg and react_native_svg no longer pass and then compile as one module. Name resolution reads the two fields it needs with the regex parser, so it adds no `pod ipc spec` spawn, and the full read is memoized on the resolved path so one run reads a podspec once across name resolution, header search paths and scaffolding. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 9682967 commit cf6a627

12 files changed

Lines changed: 924 additions & 537 deletions

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,55 @@ Each entry becomes a target in `build/generated/autolinking/Package.swift`.
352352
Sources outside `build/generated/autolinking/` are automatically mirrored with
353353
file-level symlinks.
354354

355+
## Library names
356+
357+
An autolinked library's SwiftPM target name is also the prefix its headers are
358+
imported under (`#import <RNSVG/…>`), so it is not cosmetic: it has to be the
359+
prefix the library's own sources and its dependents already use. Its podspec is
360+
where that prefix is declared, so that is where the autolinker reads it:
361+
362+
| Precedence | Source | Example |
363+
| ---------- | ---------------------------------------------------- | ------------------------------------- |
364+
| 1 | `spm.name` in the library's `react-native.config.js` | `RNSVGFork` |
365+
| 2 | podspec `header_dir` | `React-Core``React` |
366+
| 3 | podspec name | `react-native-svg``RNSVG` |
367+
| 4 | npm package name | `react-native-svg``ReactNativeSvg` |
368+
369+
`header_dir` comes first because that is what a library sets when its import
370+
prefix differs from its pod name. Step 4 only applies to a library that ships no
371+
podspec — typically one that ships its own `Package.swift` and names its targets
372+
itself.
373+
374+
An unreadable podspec falls through to the next step rather than failing the
375+
build; it is a file only CocoaPods needs. A prefix Swift cannot spell is
376+
normalized rather than abandoned — `Some.Pod` becomes `Some_Pod`, the identifier
377+
SwiftPM would compile it as anyway — with a warning naming `spm.name` as the way
378+
to choose the prefix yourself.
379+
380+
### Name collisions
381+
382+
Two kinds of name are refused: one React Native reserves for its own packages
383+
and products (`ReactNative`, `ReactHeaders`, `ReactNativeHeaders`,
384+
`ReactNativeDependenciesHeaders`, `ReactAppHeaders`, `React-GeneratedCode`,
385+
`ReactCodegen`, `ReactAppDependencyProvider`, `Autolinked`), and one another
386+
autolinked library already resolved to. Either is a **hard error** naming
387+
`spm.name` as the fix — nothing is renamed automatically, because a name the
388+
build invented is a name no `#import` in your source tree can predict.
389+
390+
Two names have to differ by more than case or punctuation to be two targets:
391+
`worklets` and `Worklets` are one directory in the headers tree, and `foo-bar`
392+
and `foo_bar` are one module, because SwiftPM replaces every character C99
393+
rejects with `_`.
394+
395+
```js
396+
// A fork whose podspec is still named RNSVG, which react-native-svg has:
397+
// react-native-svg-fork/react-native.config.js
398+
module.exports = {
399+
dependency: {platforms: {ios: {}}},
400+
spm: {name: 'RNSVGFork'},
401+
};
402+
```
403+
355404
## Dependencies between libraries
356405

357406
SwiftPM has no equivalent of a podspec's `s.dependency`, so a library that needs
@@ -490,6 +539,7 @@ across apps; refresh it with `react-native spm update --download force`.
490539
| `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`. |
491540
| `spm add` fails: "multiple .xcodeproj found" | Pass `--xcodeproj <path>` (and `--product-name <target>` if multiple app targets). |
492541
| `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) |
542+
| `SPM Swift name collision` | Two libraries resolved to one Swift name, or one took a name React Native reserves. Set `spm.name` in the library's `react-native.config.js` — see [Library names](#library-names) |
493543
| Missing headers | Re-run `react-native spm` |
494544
| "not contained in target" | Re-run setup (regenerates file-level symlinks) |
495545
| Codegen fails | Use `--skipCodegen` to iterate on other parts |

0 commit comments

Comments
 (0)