[iOS][swiftpm] Derive an SPM library's Swift name from its podspec - #58290
[iOS][swiftpm] Derive an SPM library's Swift name from its podspec#58290chrfalch wants to merge 2 commits into
Conversation
c66bafe to
9dda3a4
Compare
cipolleschi
left a comment
There was a problem hiding this comment.
Thank you for putting this together—the direction addresses a real gap. I’m requesting changes for the two concrete blockers called out inline: the current RNTester SwiftPM builds fail after the target-name change, and the fast podspec parsing can promote a subspec header_dir to the package name for external libraries. Once those are fixed with regression coverage, I’ll be happy to take another look.
| const derived = toSwiftName(npmName); | ||
| if (!reserved.has(derived.toLowerCase())) { | ||
| return derived; | ||
| const fromPodspec = podspecSwiftName(npmName, podspec); |
There was a problem hiding this comment.
Thanks for tackling this naming mismatch. This currently changes the common test library from the npm-derived ReactNativeTestLibraryCommon to its podspec name, TestLibraryCommon, while TestLibraryApple.mm still imports <ReactNativeTestLibraryCommon/TestLibraryCommon.h>. Both test_ios_spm_rntester Debug and Release fail with that header not found. Could we update the fixture/import and add regression coverage so these end-to-end SPM builds pass?
| : null; | ||
| }; | ||
| const name = field('name'); | ||
| const headerDir = field('header_dir'); |
There was a problem hiding this comment.
Thanks for adding the podspec fallback. This fast path also picks up a subspec's ss.header_dir as though it were the package-level value: the regex matches the trailing s.header_dir. For example, react-native-svg has s.name = 'RNSVG' and a nested ss.header_dir = 'rnsvg', so this resolves rnsvg even though the intended library name here is RNSVG; react-native-screens has the same shape. Could we make this scope-aware (or fall back to the pod name / explicit config) and cover these external-pod shapes in a regression test?
… config a home 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. A package's SwiftPM settings now live in `swiftpmConfig` in its package.json, following codegenConfig's conventions: `name`, `dependencies`, `autolinkingPlugin` and `scaffold` for a library, `modules` and `denyPlugins` for an app. The `spm` block in react-native.config.js is deprecated — still read, so nothing breaks, but it warns once per file and package.json wins field by field. A name resolves from `swiftpmConfig.name`, then the deprecated `spm.name`, then the podspec's `header_dir` or name, then the npm name. The podspec is thereby transitional rather than permanent: `spm scaffold` records the name it derived as `swiftpmConfig.name` in the library's package.json, so the next run needs no podspec to name it. It never overwrites a name the library already declares, never records a name guessed from the npm name, and reports what it did. A prefix Swift cannot spell is normalized to the identifier SwiftPM would compile it as, with a warning. A reserved name or two deps landing on one name is a hard error naming swiftpmConfig.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 podspec 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. rn-tester and the Apple test library move to the new location. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9dda3a4 to
e19cd06
Compare
A SwiftPM target name is also the prefix its consumers import it under, so deriving it from the podspec has to match what CocoaPods already named the module. CocoaPods resolves `module_name || c99(header_dir) || c99(name)` (cocoapods-core specification.rb); this consulted only `header_dir` and the pod name. `react-native-maps` declares `s.name = "react-native-maps"` and `s.module_name = "ReactNativeMaps"` with no `header_dir`, so it resolved to `react-native-maps` — compiled as the module `react_native_maps` — breaking every `import ReactNativeMaps` and `#import <ReactNativeMaps/...>`. The npm transform it replaced happened to agree with CocoaPods here. `header_dir` still wins, being the prefix a library sets when it differs from its pod name; `module_name` comes next; the pod name is last. The regex fast path learns `module_name` too, and declines when one is declared but not literal — without both, the fast path answers first and the new tier never runs. Also: - Both in-repo fixtures declare `swiftpmConfig.name`, so `spm scaffold` no longer writes into tracked files on every run, in CI and locally. - A podspec that yields no name warns, naming `swiftpmConfig.name`: the fallback depends on whether CocoaPods is installed, so the same library could otherwise be named differently on two machines. - Two errors named the deprecated config block; they now describe the dependency without naming a location, which is correct whichever the library uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixed - Good catch. The fast path is now anchored to the root spec. A subspec's ss.header_dir can no longer name the library. s.name = 'RNSVG' with a nested ss.header_dir = 'rnsvg' resolves RNSVG. react-native-screens behaves the same. If a value is declared but not readable, the fast path declines. pod ipc spec then resolves it. Tests: "does not take a subspec's header_dir as the library's" and "keeps the spec's own module_name, not a subspec's". |
|
Both blockers are fixed. A later review found a third. Podspec module_name was ignored. react-native-maps sets s.name = "react-native-maps" and s.module_name = "ReactNativeMaps", with no header_dir. So it resolved to react-native-maps, and the module became react_native_maps. Every import ReactNativeMaps broke. The order is now header_dir → module_name → pod name. This matches CocoaPods: module_name || c99(header_dir) || c99(name). No CI job would have caught it. RNTester has only the two fixtures. Two smaller fixes. Both fixtures declare swiftpmConfig.name, so spm scaffold no longer writes to tracked files. A podspec with no readable name now warns, because the fallback depends on CocoaPods being installed. 929 tests, 20 suites. |
Summary:
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-svgpublishesRNSVG, notReactNativeSvg) and wrong silently — no error, just headers nobody can import under the expected name.How:
This PR reads the podspec file on scaffolding, and will use the name from the podspec if available. In addition it deprecates the react-native.config.js
spmsection in favor of the library'spackage.jsonfile.Configuration
A package's SwiftPM settings now live in
swiftpmConfigin itspackage.json, followingcodegenConfig's conventions:name,dependencies,autolinkingPluginandscaffoldfor a library,modulesanddenyPluginsfor an app. Thespmblock in react-native.config.js is deprecated — still read, so nothing breaks, but it warns once per file and package.json wins field by field.Resolving
A name resolves from
swiftpmConfig.name, then the deprecatedspm.name, then the podspec'sheader_diror name, then the npm name. The podspec is thereby transitional rather than permanent:spm scaffoldrecords the name it derived asswiftpmConfig.namein the library's package.json, so the next run needs no podspec to name it. It never overwrites a name the library already declares, never records a name guessed from the npm name, and reports what it did.Failsafety
A prefix Swift cannot spell is normalized to the identifier SwiftPM would compile it as, with a warning. A reserved name or two deps landing on one name is a hard error naming swiftpmConfig.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.
Implementation
Name resolution reads the two podspec fields it needs with the regex parser, so it adds no
pod ipc specspawn, 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.rn-testerand the Apple test library move to the new location.Changelog:
[IOS] [FIXED] - Read SwiftPM name from podspec and store in package.json when scaffolding
Test Plan:
✅ Unit tests