Skip to content

feat(ios): add experimental Package.swift for React Native SPM mode - #426

Open
thomson-t wants to merge 1 commit into
thomson-t/spm-05-swift-rokt-bridgefrom
thomson-t/spm-06-package-swift
Open

thomson-t wants to merge 1 commit into
thomson-t/spm-05-swift-rokt-bridgefrom
thomson-t/spm-06-package-swift

Conversation

@thomson-t

@thomson-t thomson-t commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Why

React Native 0.87 added an experimental mode where an app uses Swift Package Manager instead of CocoaPods for everything, including React Native itself. Today that mode stops with "Package.swift is missing" as soon as an app adds this package, and React Native's generator cannot produce a working manifest for it. Once this lands, an app trying React Native's new mode can add this package and the mParticle SDK and get a working build, with one copy of each SDK. The mode is experimental in React Native itself and is documented as not for production; CocoaPods apps are unaffected.

Programme

Part of the plan to support Swift Package Manager in this package before the CocoaPods central repository becomes read-only on 2026-12-02 (CocoaPods announcement). This is the sixth of eight pull requests, all merging into the workstation/spm-migration branch, which merges into main once the series is complete, and shipping together in one release; the plan record is internal and cannot be linked here.

What changes

Before: npx react-native spm add fails for this package, and npx react-native spm scaffold generates a manifest without the mParticle and Rokt dependencies.

After: the package ships a hand-written Package.swift with two targets, because Swift Package Manager cannot mix Swift and Objective-C++ in one target:

  • RNMParticleSwift: the Swift layer from the previous pull request;
  • ReactNativeMparticle: the Objective-C++ module, with the New Architecture define that React Native's code paths need.

The mParticle and RoktContracts version ranges match the podspec. The package URLs match the ones the mParticle kits use, so Swift Package Manager treats them as one package. package.json adds the manifest to the published files. A jest test fails if a source file anywhere under ios/RNMParticle is missing from the manifest's list, because CI does not build this mode. The README gains an "Experimental: React Native Swift Package Manager mode" section with a not-for-production note.

Start reading at Package.swift. Left alone on purpose:

  • a react-native.config.js name override: React Native derives ReactNativeMparticle from the npm name, and that config file would also affect CocoaPods autolinking;
  • the prefix header React Native's generator adds: this package's sources don't need it.

Linked work

Depends on: the Swift layer (branch thomson-t/spm-05-swift-rokt-bridge) and the pull requests below it in this series; merge those first.
Unblocks: the last pull request in this series, which removes the map form of embedded placeholders (branch thomson-t/spm-07-remove-placeholder-map, stacked on this one).
Related: React Native's proposal for Swift Package Manager and CocoaPods deprecation (react-native-community/discussions-and-proposals#994).

Rollout

Path: this merges into workstation/spm-migration, not main, so nothing reaches main or a release until the whole series has merged there and that branch is merged into main. It then ships in the next release. It only takes effect for apps that use React Native's Swift Package Manager mode.
Feature flags: none; React Native's own spm add is the opt-in.
Turning it off: an app returns to CocoaPods. For the package, reverting this pull request and releasing again removes the manifest in the next version.
What we watch: this repository's issues, for build errors in React Native's Swift Package Manager mode and for any change in CocoaPods apps after upgrading.

Risks

  • CocoaPods apps could pick up the new manifest; prevented because CocoaPods reads only the podspec, which this pull request does not change. A fresh React Native 0.87.1 CocoaPods app resolved and archived normally, and the sample's Podfile.lock was unchanged; we would see changed pods or duplicate classes after upgrading.
  • The manifest's hand-written source list falls behind new files; prevented by the jest test described above; we would see that test fail in CI.
  • The local package paths depend on where React Native's autolinker puts its generated packages; not addressed beyond following the layout other React Native libraries use, because React Native does not yet publish a remote package; we would see "package not found" errors when React Native changes that layout.
  • React Native's mode itself changes or breaks between releases; not addressed, because it is experimental upstream, and the README says so; we would see build errors on newer React Native versions.

Risk class: low.

Who

Written by: an automated coding agent (Claude Code), at an engineer's request, following the internal Swift Package Manager migration plan and its proof of concept.
Code reviewed before opening: an independent review agent reviewed the change before it was committed; its advisory about the hand-written source list led to the jest test.
Design reviewed before opening: the requesting engineer approved the migration plan, which specifies a hand-written two-target manifest.
Decision this implements: the requesting engineer's approval of the migration plan on 2026-09-28; the record is internal and cannot be linked.
Checked: on 2026-09-29, with Xcode 27.0 on an iOS 26.5 simulator:

  • React Native's Swift Package Manager mode: a fresh React Native 0.87.1 app with this package's npm tarball.
    • npx react-native spm add --deintegrate --yes found the package.
    • The mParticle core and Rokt kit 9.6.1 were added to the app target.
    • A Release build and an unsigned archive succeeded, with one copy of each SDK class.
    • Embedded placements by name and by tag, and an overlay placement, rendered.
  • CocoaPods mode: a fresh React Native 0.87.1 app archived with one copy of each SDK class, and the sample app's Podfile.lock was unchanged.
    Not checked: React Native 0.88, which is still a release candidate; Expo's Swift Package Manager autolinking preview (Expo 58), since the test app is on Expo 57; physical devices.

Size

Hand-written: 110 lines added and 1 removed in 4 files (24 of them the test).
Generated: none.

🤖 Generated with Claude Code

@thomson-t
thomson-t added this pull request to stack #427 September 29, 2026 21:25
@thomson-t
thomson-t marked this pull request as ready for review September 30, 2026 13:20
@thomson-t
thomson-t requested a review from a team as a code owner September 30, 2026 13:20
Copilot AI balanced review requested due to automatic review settings September 30, 2026 13:20
@cursor

cursor Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Low risk as this adds an opt-in SwiftPM manifest and documentation for experimental React Native SPM setups, leaving existing CocoaPods integration unaffected.

Overview
Adds an experimental Package.swift manifest to support React Native's new Swift Package Manager mode (React Native 0.87+). The manifest splits the implementation into two targets—RNMParticleSwift and ReactNativeMparticle—to support both Swift and Objective-C++ sources.

Includes Package.swift in the npm package payload, adds a unit test to keep the manifest's source list in sync with files in ios/RNMParticle, and updates README.md with SPM setup instructions.

Reviewed by Cursor Bugbot for commit 3cd997b. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The source-list regression test does not detect nested Objective-C or Objective-C++ files included by the podspec.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds experimental Swift Package Manager support for React Native 0.87+ while preserving CocoaPods behavior.

Changes:

  • Adds a two-target Swift package manifest with mParticle/Rokt dependencies.
  • Publishes the manifest and documents experimental setup.
  • Adds source-list synchronization coverage.
File Description
Package.swift Defines Swift and Objective-C++ targets and dependencies.
package.json Includes the manifest in npm releases.
README.md Documents experimental SPM setup.
js/​__tests__/​package-swift-sources.test.ts Checks the hand-maintained source list.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +12 to +20
const listed = new Set(
[...manifest.matchAll(/"(ios\/RNMParticle\/[^"/]+\.(?:h|m|mm))"/g)].map(
match => match[1]
)
);
const onDisk = fs
.readdirSync(path.join(root, 'ios', 'RNMParticle'))
.filter(file => /\.(h|m|mm)$/.test(file))
.map(file => `ios/RNMParticle/${file}`);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3cd997b. The test now walks ios/RNMParticle recursively (readdirSync with recursive: true), and the manifest regex accepts nested paths. Adding a nested .m file now fails the test.

React Native 0.87's Swift Package Manager mode stops with "Package.swift is
missing" for this library, and `spm scaffold` drops its native
dependencies. This hand-written manifest has two targets, since SwiftPM
cannot mix languages in one: the Swift layer, and the Objective-C++ module
with RCT_NEW_ARCH_ENABLED defined. The mParticle and RoktContracts version
ranges match the podspec, and the package URLs match the kits'.

The manifest is shipped in the npm package. A jest test keeps its source
list in step with ios/RNMParticle, because CI does not build it. The
README describes the mode as experimental and not for production.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@thomson-t
thomson-t force-pushed the thomson-t/spm-06-package-swift branch from f63aa19 to 3cd997b Compare September 30, 2026 14:02
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.

2 participants