Skip to content

chore(react-native-plugin)!: resolve posthog-ios through Swift Package Manager - #5449

Merged
turnipdabeets merged 6 commits into
chore/rn-plugin-v3from
chore/rn-plugin-spm-only
Oct 30, 2026
Merged

turnipdabeets merged 6 commits into
chore/rn-plugin-v3from
chore/rn-plugin-spm-only

Conversation

@turnipdabeets

Copy link
Copy Markdown
Contributor

Problem

posthog-ios 4.0 ships on Swift Package Manager only (PostHog/posthog-ios#972). The plugin's podspec gets posthog-ios from CocoaPods trunk by default (s.dependency 'PostHog'), and only uses SwiftPM when the app sets posthog.useSpm. Trunk will never have 4.x, so with the 4.0 pin (#5237) the default pod install fails. Stacked on #5194 (plugin 3.0).

Changes

Change Why
The podspec always adds posthog-ios with React Native's spm_dependency. It no longer depends on the PostHog pod or reads posthog.useSpm. Trunk can't serve 4.x. posthog_ios_version stays the one source for the version.
react-native peer >=0.71 → >=0.75. The podspec raises a clear error when spm_dependency is missing. spm_dependency first ships in react-native 0.75.0 and react-native-macos 0.75 (checked in the npm tarballs: absent in 0.74.7 / macOS 0.74.5). The 0.71 changeset from #5194 is edited to 0.75.
Stop setting sessionReplayConfig.screenshotModeBackgroundCapture on iOS. posthog-ios 4.0 removes it (PostHog/posthog-ios#937), so the plugin no longer compiles against 4.0. The posthog-react-native option is now ignored on iOS.
CI: the posthog.useSpm lane becomes a dynamic-frameworks lane. SwiftPM is now the default, so the static-library lane already covers it.

Dynamic frameworks aren't required. The old podspec comment said the SwiftPM path needs use_frameworks! :linkage => :dynamic. With current React Native and Xcode 26 that isn't true: static libraries, static frameworks and dynamic frameworks all build and run (see below). pod install still prints React Native's generic "static linking might cause linker errors" warning for static linkage. Expo apps don't need expo-build-properties changes.

Merge order

For the maintainer

Release info Sub-libraries affected

Libraries affected

  • All of them
  • posthog-js (web)
  • posthog-js-lite (web lite)
  • posthog-node
  • posthog-react-native
  • @posthog/react-native-plugin
  • @posthog/react
  • @posthog/ai
  • @posthog/convex
  • @posthog/next
  • @posthog/nextjs-config
  • @posthog/nuxt
  • @posthog/openfeature-node-provider
  • @posthog/openfeature-web-provider
  • @posthog/rollup-plugin
  • @posthog/webpack-plugin
  • @posthog/types
  • @posthog/browser-common

Checklist

  • All commits are signed (see Commit signing)
  • Tests for new code (build-only change; covered by the native CI lanes)
  • Accounted for the impact of any changes across different platforms
  • Accounted for backwards compatibility of any changes (no breaking changes!) (intentional, part of the 3.0 major)
  • Took care not to unnecessarily increase the bundle size

If releasing new changes

  • Ran pnpm changeset to generate a changeset file. Added rn-plugin-v3-spm.md and rn-plugin-v3-screenshot-background-capture.md, and edited the RN floor changeset (renamed to rn-plugin-v3-react-native-075.md).

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Claude Code (Opus 5.5), run as a subagent from Anna's session. Tools: git, gh, pnpm, CocoaPods, xcodebuild, simctl.

Local builds used Xcode 26.4.1, with #5194 + #5237 + #5236 merged and posthog-ios pointed at #972's branch (chore/v4-spm-only @ d079f97) through a temporary spm_dependency branch requirement. That override isn't committed.

Check Result
examples/example-rn-native-plugin (RN 0.79.6), iOS simulator: static libraries (default), USE_FRAMEWORKS=static, USE_FRAMEWORKS=dynamic pod install + Debug build pass for all three; Release passes for static. Each launches, and a temporary setup() call resolves with replay buffering in screenshot capture mode. CrashReporter and libwebp are linked, so default traits are on.
Same example, macOS (react-native-macos 0.79.4) pod install + Debug build pass.
examples/example-expo-57 (Expo 57, RN 0.86.2, static libraries, precompiled modules) with packed local tarballs expo prebuild + pod install + simulator Debug build pass. Not launched.
Podspec with no spm_dependency (pod ipc spec) Fails with the new "requires React Native 0.75" message.
Plugin pnpm lint, pnpm typecheck, pnpm test:unit; actionlint on the workflow Pass

Not verified: React Native's full-SwiftPM path (examples/example-rn-spm) against 4.0 with #5237's traits (this PR doesn't touch Package.swift); Release builds other than static; devices; RN 0.75–0.78. Local tooling needed workarounds that don't come from the plugin (Ruby 4 + json 3 broke Expo's pod install, and the CommandLineTools SDK broke native compiles).

🤖 Generated with Claude Code

…e Manager

posthog-ios 4.0 ships on Swift Package Manager only, so the podspec always
adds posthog-ios with React Native's spm_dependency helper instead of
depending on the PostHog pod from CocoaPods trunk. That helper needs
react-native / react-native-macos 0.75, so the peer floor moves from 0.71.

Also stop setting screenshotModeBackgroundCapture, which posthog-ios 4.0
removes, and turn the posthog.useSpm CI lane into a dynamic-frameworks lane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@posthog posthog Bot 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.

PostHog Review · Standard · reviewed 6aafa73: 1 Should fix.

See this review and its findings in PostHog Review. Comment @posthog review --deep for a Deep review. A Deep review takes longer and looks deeper than a Standard review.

@github-actions

Copy link
Copy Markdown
Contributor

Coherence

0 spec problems · 0 failing chokepoints · 2 spec gaps · 0 guard failures

44 components, 7 bullets (5 invariants, 2 requirements), 0 problems

Chokepoints: none declared.

Spec gaps: 2 entrances with outside or unknown trust and no traced control, and 10 more held by the adoption baseline

  • packages/web/Web.spec.md entrance public api (trust host-app): packages/web
  • packages/webpack-plugin/Webpack-plugin.spec.md entrance webpack plugin (trust build): packages/webpack-plugin

Close one with guard: <chokepoint>, an invariant that names it on its entrances: line, or control: none — <reason>; scaffold control proposes it.

… isn't supported

React Native's spm_dependency only searches the main Pods project for the plugin's target, so pod install crashed with that option on.
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Node SDK compliance v2

Commit: f392811
Workflow run and artifacts

Capture v0 — acceptance

✅ 28 passed / ❌ 3 non-passing / ⚠️ 853 not selected

Drill-down: run artifacts → node-v2-v0 → acceptance/report.json.diagnostics.json (match case_id below).

❌ server_snapshot_payload_false — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:
❌ server_snapshot_payload_zero — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:
❌ server_snapshot_payload_null — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:

Harness exit: 1; report validation exit: 1.

Capture v1 — acceptance

✅ 28 passed / ❌ 3 non-passing / ⚠️ 853 not selected

Drill-down: run artifacts → node-v2-v1 → acceptance/report.json.diagnostics.json (match case_id below).

❌ server_snapshot_payload_false — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:
❌ server_snapshot_payload_zero — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:
❌ server_snapshot_payload_null — failed_assertion

Expected native data result

Code: snapshot_result

Failed step: acceptance/public/evaluate-flags.feature:868

the public snapshot outcomes should have these semantics:

Harness exit: 1; report validation exit: 1.

Capture v0 — migration

✅ 55 passed / ❌ 1 non-passing / ⚠️ 101 not selected

Drill-down: run artifacts → node-v2-v0 → reports/report.json.diagnostics.json (match case_id below).

❌ migration/yaml-parity-v1/remote-flags-v1.feature::Omitted GeoIP settings default to enabled in flag requests — failed_assertion

Flags field differs

Code: flag_request_field

Failed step: migration/yaml-parity-v1/remote-flags-v1.feature:134

the first flags request field "geoip_disable" should equal JSON false

Operation: /flags/

Field: geoip_disable

Expected:

false

Actual value:

true

Harness exit: 1; report validation exit: 1.

Capture v1 — migration

✅ 120 passed / ❌ 1 non-passing / ⚠️ 36 not selected

Drill-down: run artifacts → node-v2-v1 → reports/report.json.diagnostics.json (match case_id below).

❌ migration/yaml-parity-v1/remote-flags-v1.feature::Omitted GeoIP settings default to enabled in flag requests — failed_assertion

Flags field differs

Code: flag_request_field

Failed step: migration/yaml-parity-v1/remote-flags-v1.feature:134

the first flags request field "geoip_disable" should equal JSON false

Operation: /flags/

Field: geoip_disable

Expected:

false

Actual value:

true

Harness exit: 1; report validation exit: 1.

@dustinbyrne dustinbyrne 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.

The multi-project diagnostic addresses the earlier concern. Disabled target deduplication still reaches a missing-target failure; there is also an optional Podfile re-entry improvement inline.

AI-assisted review.

@dustinbyrne
dustinbyrne requested a review from a team October 27, 2026 21:30
turnipdabeets and others added 2 commits October 27, 2026 18:15
…sn't supported

With target deduplication off, CocoaPods names the plugin's target posthog-react-native-plugin-Pods-<App>, and React Native's spm_dependency looks it up by its exact pod name, so pod install crashed on a nil target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…spec

Autolinking evaluates the podspec while the Podfile is still loading, so Pod::Config#podfile loaded the Podfile again from the plugin directory, re-running use_native_modules! and its CLI config command. Read the already-loaded Podfile instead; pod install re-evaluates the :path podspec afterwards, so the install! option checks still run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
turnipdabeets added a commit to PostHog/posthog.com that referenced this pull request Oct 28, 2026
Covers PostHog/posthog-js#5449: posthog-ios always comes from SwiftPM, and screenshotModeBackgroundCapture is ignored on iOS.

@dustinbyrne dustinbyrne 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.

Both previous findings are addressed. One multi-application case with different iOS deployment targets still reaches the SPM missing-target failure; details inline.

AI-assisted review.

@dustinbyrne
dustinbyrne requested a review from a team October 28, 2026 19:26
…tforms or linkage aren't supported

CocoaPods then splits the plugin into scoped targets (posthog-react-native-plugin-iOS15.1, -framework/-library), and React Native's spm_dependency crashed on a nil target looking up the exact pod name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@dustinbyrne dustinbyrne 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.

The concrete-target diagnostics are verified. My earlier rooted example was incorrect: App16 does not inherit the plugin. One abstract-parent variant still escapes the guard; details inline.

AI-assisted review.

@dustinbyrne
dustinbyrne requested a review from a team October 29, 2026 16:53
…oped pods

An abstract parent with a platform gets its own plugin variant, so a different linkage there splits the target too.

@dustinbyrne dustinbyrne 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.

Verified the abstract-target diagnostic and aligned-target controls. The earlier findings are addressed.

AI-assisted review.

@turnipdabeets
turnipdabeets merged commit 15dd448 into chore/rn-plugin-v3 Oct 30, 2026
74 checks passed
@turnipdabeets
turnipdabeets deleted the chore/rn-plugin-spm-only branch October 30, 2026 19:07
turnipdabeets added a commit that referenced this pull request Oct 31, 2026
…e Manager (#5449)

* chore(react-native-plugin)!: resolve posthog-ios through Swift Package Manager

posthog-ios 4.0 ships on Swift Package Manager only, so the podspec always
adds posthog-ios with React Native's spm_dependency helper instead of
depending on the PostHog pod from CocoaPods trunk. That helper needs
react-native / react-native-macos 0.75, so the peer floor moves from 0.71.

Also stop setting screenshotModeBackgroundCapture, which posthog-ios 4.0
removes, and turn the posthog.useSpm CI lane into a dynamic-frameworks lane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(react-native-plugin): explain that generate_multiple_pod_projects isn't supported

React Native's spm_dependency only searches the main Pods project for the plugin's target, so pod install crashed with that option on.

* fix(react-native-plugin): explain that deduplicate_targets => false isn't supported

With target deduplication off, CocoaPods names the plugin's target posthog-react-native-plugin-Pods-<App>, and React Native's spm_dependency looks it up by its exact pod name, so pod install crashed on a nil target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(react-native-plugin): don't reload the app's Podfile from the podspec

Autolinking evaluates the podspec while the Podfile is still loading, so Pod::Config#podfile loaded the Podfile again from the plugin directory, re-running use_native_modules! and its CLI config command. Read the already-loaded Podfile instead; pod install re-evaluates the :path podspec afterwards, so the install! option checks still run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(react-native-plugin): explain that app targets with different platforms or linkage aren't supported

CocoaPods then splits the plugin into scoped targets (posthog-react-native-plugin-iOS15.1, -framework/-library), and React Native's spm_dependency crashed on a nil target looking up the exact pod name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(react-native-plugin): count abstract targets when checking for scoped pods

An abstract parent with a platform gets its own plugin variant, so a different linkage there splits the target too.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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