Skip to content

React Native: Add Swift Package Manager support - #1076

Merged
simolus3 merged 6 commits into
mainfrom
swiftpm
Aug 19, 2026
Merged

simolus3 merged 6 commits into
mainfrom
swiftpm

Conversation

@simolus3

@simolus3 simolus3 commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

React Native version 0.87.0 added experimental support for Swift Package Manager to build iOS apps.

This adds ios/Package.swift with an empty Swift target depending on the core extension to ensure those builds will bundle it. The main challenge here was figuring out how to name the package. By default, autolinking in React Native uses the unqualified part of a package name and shortens scopes around hyphens. So @react-native/foo would be named RNFoo, for example. Unfortunately, the expected default name for @powersync/react-native is simply ReactNative, which conflicts with React Native itself.

To fix the package name picked up by autolinking, we can write a react-native.config.js file. These options appear to be undocumented and actively contradict the design doc for SwiftPM integration in React Native. Additionally, some components of the React Native build process read that file via await import() while others require() it, which requires additional workarounds. So the whole thing is a huge mess, but it works and with React Native, that's as good as it gets.

I have tested these changes by upgrading tools/powersynctests to React Native 0.87.0 and running iOS and Android tests locally. To ensure the react-native.config.js change doesn't break older apps, I've also tested react-native-supabase-todolist which keeps using React Native 0.86.0 and CocoaPods.

AI use: Scanned build files with Claude Code, used that to fix some minor issues.

@changeset-bot

changeset-bot Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5e8d8ee

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@powersync/react-native Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@changeset-bot

This comment was marked as resolved.

@simolus3
simolus3 marked this pull request as ready for review August 18, 2026 15:48
@simolus3
simolus3 requested a review from Chriztiaan August 18, 2026 15:48
@simolus3
simolus3 merged commit c6e7343 into main Aug 19, 2026
16 of 17 checks passed
@simolus3
simolus3 deleted the swiftpm branch August 19, 2026 10:24
meta-codesync Bot pushed a commit to react/react-native that referenced this pull request Aug 21, 2026
Summary:
After community feedback from this one powersync-ja/powersync-js#1076 we've decided to fix the issue of clashing name constants - to avoid the SwiftPM pipeline scaffolding packages that collides with built-in packages.

This PR is the first step fixing this - refactoring the names of internal packages into constants.

## Changelog:

[IOS] [FIXED] - Created constants for internal SwiftPM packages so that we can create guards to avoid collisions

Pull Request resolved: #58035

Test Plan: ✅ Unit tests

Reviewed By: Abbondanzo

Differential Revision: D116936194

Pulled By: cipolleschi

fbshipit-source-id: 2882e512489cc4784a09aeb03769e97d64d286f7
meta-codesync Bot pushed a commit to react/react-native that referenced this pull request Aug 24, 2026
Summary:
When saving settings in `react-native.config.js`, we should support all styles of exported data:

```js
module.exports = { spm: { name: 'worklets' } };   // old style
export const spm = { name: 'worklets' };          // new style, with a label
export default { spm: { name: 'worklets' } };     // new style, no label — "just this"
```

We only read the two first in the SwiftPM pipeline, silently ignoring anything written with `export default`.

This PR fixes this by adding a catch that tries to decode the default export.

Fixed after community feedback here: powersync-ja/powersync-js#1076

## Changelog:

[IOS] [FIXED] - SwiftPM pipeline now reads react-native.config.js with default exports correctly

Pull Request resolved: #58034

Test Plan: ✅ Added and ran unit tests.

Reviewed By: christophpurrer

Differential Revision: D116935856

Pulled By: cipolleschi

fbshipit-source-id: 7ebf7af3b3aaee2ffb506f94a15fabc244a4db30
meta-codesync Bot pushed a commit to react/react-native that referenced this pull request Aug 24, 2026
Summary:
The scaffolder derived every target name with `toSwiftName(dep.name)`, ignoring the `spm.name` override in the library's own react-native.config.js.

A library setting that override got a manifest whose product name differed from the name the autolinker registers it under, and SPM failed resolution on `.product(name: "X", package: "X")` — the exact mismatch the override exists to prevent.

This setting is documented - but not followed up in the implementation.

Use the name the autolinker already resolved (`dep.swiftName`), falling back to `toSwiftName` when a caller runs the translation without it. Sibling references resolve the same way, through the new `SpmScaffoldSpec.siblingSwiftNames`.

Fixed after community feedback here: powersync-ja/powersync-js#1076

## Changelog:

[IOS] [FIXED] - Fixed not reading spm.name from react-native.config.js when scaffolding the Swift manifest

Pull Request resolved: #58033

Test Plan: ✅ unit tests

Reviewed By: zeyap

Differential Revision: D116794063

Pulled By: cipolleschi

fbshipit-source-id: 8b2f204efbc4d7537839ccef5b90cf4eba0d27bf
meta-codesync Bot pushed a commit to react/react-native that referenced this pull request Aug 25, 2026
Summary:
A dependency's Swift name is derived from its npm package name with the scope dropped, which makes two collisions unavoidable: `powersync/react-native` derives `ReactNative`, one of React Native's own names, and `a/foo` and `b/foo` both derive `Foo`.

Either was emitted into the package graph as-is, and SwiftPM then failed deep inside dependency resolution with a duplicate-name error that named neither the library nor the react-native.config.js that caused it.

This was seen in the PR here powersync-ja/powersync-js#1076 - and was hard to fix.

This PR fixes this by adding support for prefixing the name with the scope if the name without scope crashes.

In addition the same logic is added when two packages have names that collide with each other.

If the name given by the SwiftPM scaffolder doesn't work for you - you can use the `spm.name` field in the `react-native.config.js` file to set a specific name (see SwiftPM docs).

## Changelog:

[IOS] [FIXED] - Resolve Swift manifest naming collisions

Pull Request resolved: #58044

Test Plan: ✅ Unit tests green

Reviewed By: mdvacca, cortinico

Differential Revision: D117178818

Pulled By: cipolleschi

fbshipit-source-id: 23269f4d959b84ac484be1911e8a863c74008ff9
cipolleschi pushed a commit to react/react-native that referenced this pull request Aug 26, 2026
Summary:
After community feedback from this one powersync-ja/powersync-js#1076 we've decided to fix the issue of clashing name constants - to avoid the SwiftPM pipeline scaffolding packages that collides with built-in packages.

This PR is the first step fixing this - refactoring the names of internal packages into constants.

## Changelog:

[IOS] [FIXED] - Created constants for internal SwiftPM packages so that we can create guards to avoid collisions

Pull Request resolved: #58035

Test Plan: ✅ Unit tests

Reviewed By: Abbondanzo

Differential Revision: D116936194

Pulled By: cipolleschi

fbshipit-source-id: 2882e512489cc4784a09aeb03769e97d64d286f7
(cherry picked from commit d36aa49)
cipolleschi pushed a commit to react/react-native that referenced this pull request Aug 26, 2026
Summary:
When saving settings in `react-native.config.js`, we should support all styles of exported data:

```js
module.exports = { spm: { name: 'worklets' } };   // old style
export const spm = { name: 'worklets' };          // new style, with a label
export default { spm: { name: 'worklets' } };     // new style, no label — "just this"
```

We only read the two first in the SwiftPM pipeline, silently ignoring anything written with `export default`.

This PR fixes this by adding a catch that tries to decode the default export.

Fixed after community feedback here: powersync-ja/powersync-js#1076

[IOS] [FIXED] - SwiftPM pipeline now reads react-native.config.js with default exports correctly

Pull Request resolved: #58034

Test Plan: ✅ Added and ran unit tests.

Reviewed By: christophpurrer

Differential Revision: D116935856

Pulled By: cipolleschi

fbshipit-source-id: 7ebf7af3b3aaee2ffb506f94a15fabc244a4db30
(cherry picked from commit ed76640)
cipolleschi pushed a commit to react/react-native that referenced this pull request Aug 26, 2026
Summary:
The scaffolder derived every target name with `toSwiftName(dep.name)`, ignoring the `spm.name` override in the library's own react-native.config.js.

A library setting that override got a manifest whose product name differed from the name the autolinker registers it under, and SPM failed resolution on `.product(name: "X", package: "X")` — the exact mismatch the override exists to prevent.

This setting is documented - but not followed up in the implementation.

Use the name the autolinker already resolved (`dep.swiftName`), falling back to `toSwiftName` when a caller runs the translation without it. Sibling references resolve the same way, through the new `SpmScaffoldSpec.siblingSwiftNames`.

Fixed after community feedback here: powersync-ja/powersync-js#1076

## Changelog:

[IOS] [FIXED] - Fixed not reading spm.name from react-native.config.js when scaffolding the Swift manifest

Pull Request resolved: #58033

Test Plan: ✅ unit tests

Reviewed By: zeyap

Differential Revision: D116794063

Pulled By: cipolleschi

fbshipit-source-id: 8b2f204efbc4d7537839ccef5b90cf4eba0d27bf
(cherry picked from commit 482acee)
cipolleschi pushed a commit to react/react-native that referenced this pull request Aug 26, 2026
Summary:
A dependency's Swift name is derived from its npm package name with the scope dropped, which makes two collisions unavoidable: `powersync/react-native` derives `ReactNative`, one of React Native's own names, and `a/foo` and `b/foo` both derive `Foo`.

Either was emitted into the package graph as-is, and SwiftPM then failed deep inside dependency resolution with a duplicate-name error that named neither the library nor the react-native.config.js that caused it.

This was seen in the PR here powersync-ja/powersync-js#1076 - and was hard to fix.

This PR fixes this by adding support for prefixing the name with the scope if the name without scope crashes.

In addition the same logic is added when two packages have names that collide with each other.

If the name given by the SwiftPM scaffolder doesn't work for you - you can use the `spm.name` field in the `react-native.config.js` file to set a specific name (see SwiftPM docs).

[IOS] [FIXED] - Resolve Swift manifest naming collisions

Pull Request resolved: #58044

Test Plan: ✅ Unit tests green

Reviewed By: mdvacca, cortinico

Differential Revision: D117178818

Pulled By: cipolleschi

fbshipit-source-id: 23269f4d959b84ac484be1911e8a863c74008ff9
(cherry picked from commit 962aeec)
tvinhas pushed a commit to tvinhas/react-native-macos that referenced this pull request Oct 21, 2026
…58035)

Summary:
After community feedback from this one powersync-ja/powersync-js#1076 we've decided to fix the issue of clashing name constants - to avoid the SwiftPM pipeline scaffolding packages that collides with built-in packages.

This PR is the first step fixing this - refactoring the names of internal packages into constants.

## Changelog:

[IOS] [FIXED] - Created constants for internal SwiftPM packages so that we can create guards to avoid collisions

Pull Request resolved: react#58035

Test Plan: ✅ Unit tests

Reviewed By: Abbondanzo

Differential Revision: D116936194

Pulled By: cipolleschi

fbshipit-source-id: 2882e512489cc4784a09aeb03769e97d64d286f7
tvinhas pushed a commit to tvinhas/react-native-macos that referenced this pull request Oct 21, 2026
…t#58034)

Summary:
When saving settings in `react-native.config.js`, we should support all styles of exported data:

```js
module.exports = { spm: { name: 'worklets' } };   // old style
export const spm = { name: 'worklets' };          // new style, with a label
export default { spm: { name: 'worklets' } };     // new style, no label — "just this"
```

We only read the two first in the SwiftPM pipeline, silently ignoring anything written with `export default`.

This PR fixes this by adding a catch that tries to decode the default export.

Fixed after community feedback here: powersync-ja/powersync-js#1076

## Changelog:

[IOS] [FIXED] - SwiftPM pipeline now reads react-native.config.js with default exports correctly

Pull Request resolved: react#58034

Test Plan: ✅ Added and ran unit tests.

Reviewed By: christophpurrer

Differential Revision: D116935856

Pulled By: cipolleschi

fbshipit-source-id: 7ebf7af3b3aaee2ffb506f94a15fabc244a4db30
tvinhas pushed a commit to tvinhas/react-native-macos that referenced this pull request Oct 21, 2026
…58033)

Summary:
The scaffolder derived every target name with `toSwiftName(dep.name)`, ignoring the `spm.name` override in the library's own react-native.config.js.

A library setting that override got a manifest whose product name differed from the name the autolinker registers it under, and SPM failed resolution on `.product(name: "X", package: "X")` — the exact mismatch the override exists to prevent.

This setting is documented - but not followed up in the implementation.

Use the name the autolinker already resolved (`dep.swiftName`), falling back to `toSwiftName` when a caller runs the translation without it. Sibling references resolve the same way, through the new `SpmScaffoldSpec.siblingSwiftNames`.

Fixed after community feedback here: powersync-ja/powersync-js#1076

## Changelog:

[IOS] [FIXED] - Fixed not reading spm.name from react-native.config.js when scaffolding the Swift manifest

Pull Request resolved: react#58033

Test Plan: ✅ unit tests

Reviewed By: zeyap

Differential Revision: D116794063

Pulled By: cipolleschi

fbshipit-source-id: 8b2f204efbc4d7537839ccef5b90cf4eba0d27bf
tvinhas pushed a commit to tvinhas/react-native-macos that referenced this pull request Oct 21, 2026
Summary:
A dependency's Swift name is derived from its npm package name with the scope dropped, which makes two collisions unavoidable: `powersync/react-native` derives `ReactNative`, one of React Native's own names, and `a/foo` and `b/foo` both derive `Foo`.

Either was emitted into the package graph as-is, and SwiftPM then failed deep inside dependency resolution with a duplicate-name error that named neither the library nor the react-native.config.js that caused it.

This was seen in the PR here powersync-ja/powersync-js#1076 - and was hard to fix.

This PR fixes this by adding support for prefixing the name with the scope if the name without scope crashes.

In addition the same logic is added when two packages have names that collide with each other.

If the name given by the SwiftPM scaffolder doesn't work for you - you can use the `spm.name` field in the `react-native.config.js` file to set a specific name (see SwiftPM docs).

## Changelog:

[IOS] [FIXED] - Resolve Swift manifest naming collisions

Pull Request resolved: react#58044

Test Plan: ✅ Unit tests green

Reviewed By: mdvacca, cortinico

Differential Revision: D117178818

Pulled By: cipolleschi

fbshipit-source-id: 23269f4d959b84ac484be1911e8a863c74008ff9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants