Skip to content

refactor(ios): Migrate from PrivateSentrySDKOnly to SentrySDK.internal#6380

Open
alwx wants to merge 13 commits into
mainfrom
alwx/refactor/migrate-private-sentry-sdk-only
Open

refactor(ios): Migrate from PrivateSentrySDKOnly to SentrySDK.internal#6380
alwx wants to merge 13 commits into
mainfrom
alwx/refactor/migrate-private-sentry-sdk-only

Conversation

@alwx

@alwx alwx commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Refactoring

📜 Description

Replace deprecated PrivateSentrySDKOnly SPI and SentrySwizzle.h macro usage in our iOS code with the new SentrySDK.internal Swift API exposed by sentry-cocoa 9.19.0, via a thin in-pod ObjC↔Swift bridge (RNSentryInternal.swift).

The bridge uses @_spi(Private) import Sentry to reach SPI-gated sub-APIs (performance.currentScreenFrames, replay.configure, envelope.{store,capture,deserialize}). Existing .m/.mm callers route through [RNSentryInternal …] via the auto-generated RNSentry-Swift.h. The performSelector:@selector(getReplayIntegration) workaround in captureReplayWithReturnValue is gone — SentrySDK.internal.replay.capture() returns Bool directly.

Notably, we do not use sentry-cocoa's new SentryObjC umbrella module (which the issue suggested) — that module isn't shipped via the Sentry CocoaPod in 9.19.0, only via SPM as a separate xcframework. The Swift bridge works equally on both consumption paths.

💡 Motivation and Context

Closes #6370. PrivateSentrySDKOnly is deprecated in sentry-cocoa 9.19.0 and slated for removal in the next major. Migrating now avoids future churn and drops the private header search path / performSelector: reliance.

💚 How did you test it?

  • yarn build, yarn test, yarn lint, yarn circularDepCheck — all green
  • xcodebuild for the RNSentry pod target (iphonesimulator) — builds clean
  • xcodebuild test for RNSentryCocoaTester — 158/158 native Cocoa tests pass

📝 Checklist

  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • All tests passing
  • No breaking changes

🔮 Next steps

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • refactor(ios): Migrate from PrivateSentrySDKOnly to SentrySDK.internal by alwx in #6380
  • feat(ios): Consume sentry-cocoa as a prebuilt xcframework by default by alwx in #6413
  • feat(tracing): Add extend app start API for standalone app start by antonis in #6392
  • fix(ios): Skip source maps upload on Debug builds by antonis in #6405
  • fix(ci): Bump codeql-action/init to v4.36.3 to match analyze/autobuild by antonis in #6410
  • Revert "feat(ios): Default to consuming sentry-cocoa as an xcframework" (feat(ios): Default to consuming sentry-cocoa as an xcframework #6381) by alwx in #6412

🤖 This preview updates automatically when you update the PR.

@alwx

alwx commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread packages/core/ios/RNSentryInternal.swift
@alwx alwx force-pushed the alwx/refactor/migrate-private-sentry-sdk-only branch from b850f46 to 330236b Compare July 2, 2026 07:25
@alwx alwx marked this pull request as ready for review July 2, 2026 07:26
@antonis antonis added the ready-to-merge Triggers the full CI test suite label Jul 2, 2026
@sentry

sentry Bot commented Jul 2, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
Sentry RN io.sentry.reactnative.sample 8.17.2 (97) Release

⚙️ sentry-react-native Build Distribution Settings

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3839.57 ms 1223.44 ms -2616.12 ms
Size 4.98 MiB 6.48 MiB 1.50 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
d0e3b3e+dirty 3854.77 ms 1218.58 ms -2636.19 ms
5125c43+dirty 3846.45 ms 1221.12 ms -2625.32 ms
580fb5c+dirty 3836.13 ms 1218.72 ms -2617.41 ms
7887847+dirty 3849.83 ms 1227.35 ms -2622.48 ms
a216cb9+dirty 3839.33 ms 1225.26 ms -2614.07 ms
40c9884+dirty 3837.72 ms 1226.61 ms -2611.11 ms
a5d243c+dirty 3842.35 ms 1214.29 ms -2628.06 ms
f170ec3+dirty 3822.26 ms 1218.33 ms -2603.93 ms
68672fc+dirty 3841.58 ms 1228.89 ms -2612.69 ms
ae37560+dirty 3832.10 ms 1219.09 ms -2613.02 ms

App size

Revision Plain With Sentry Diff
d0e3b3e+dirty 4.98 MiB 6.51 MiB 1.53 MiB
5125c43+dirty 5.15 MiB 6.68 MiB 1.53 MiB
580fb5c+dirty 4.98 MiB 6.46 MiB 1.48 MiB
7887847+dirty 4.98 MiB 6.46 MiB 1.48 MiB
a216cb9+dirty 4.98 MiB 6.51 MiB 1.53 MiB
40c9884+dirty 4.98 MiB 6.51 MiB 1.53 MiB
a5d243c+dirty 5.15 MiB 6.68 MiB 1.53 MiB
f170ec3+dirty 5.15 MiB 6.69 MiB 1.53 MiB
68672fc+dirty 5.15 MiB 6.71 MiB 1.55 MiB
ae37560+dirty 5.15 MiB 6.70 MiB 1.54 MiB

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3844.59 ms 1231.47 ms -2613.12 ms
Size 4.98 MiB 6.48 MiB 1.50 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
580fb5c+dirty 3835.45 ms 1218.61 ms -2616.84 ms
d0e3b3e+dirty 3846.64 ms 1226.27 ms -2620.38 ms
a216cb9+dirty 3853.25 ms 1224.77 ms -2628.48 ms
37a2091+dirty 3865.27 ms 1223.92 ms -2641.35 ms
40c9884+dirty 3826.11 ms 1217.04 ms -2609.07 ms
4e0ba9c+dirty 3856.39 ms 1234.44 ms -2621.95 ms
0b5a379+dirty 3857.69 ms 1230.34 ms -2627.35 ms
68672fc+dirty 3832.22 ms 1228.29 ms -2603.93 ms
2c735cc+dirty 1223.33 ms 1224.38 ms 1.04 ms
6acdf1d+dirty 3835.35 ms 1218.30 ms -2617.06 ms

App size

Revision Plain With Sentry Diff
580fb5c+dirty 4.98 MiB 6.46 MiB 1.48 MiB
d0e3b3e+dirty 4.98 MiB 6.51 MiB 1.53 MiB
a216cb9+dirty 4.98 MiB 6.51 MiB 1.53 MiB
37a2091+dirty 5.15 MiB 6.70 MiB 1.54 MiB
40c9884+dirty 4.98 MiB 6.51 MiB 1.53 MiB
4e0ba9c+dirty 5.15 MiB 6.67 MiB 1.51 MiB
0b5a379+dirty 5.15 MiB 6.70 MiB 1.54 MiB
68672fc+dirty 5.15 MiB 6.71 MiB 1.55 MiB
2c735cc+dirty 3.38 MiB 4.74 MiB 1.35 MiB
6acdf1d+dirty 4.98 MiB 6.51 MiB 1.53 MiB

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

Overall looks good. I think a conditional import like should fix the build issues 🤞

  #if __has_include(<RNSentry/RNSentry-Swift.h>)
  #import <RNSentry/RNSentry-Swift.h>
  #else
  #import "RNSentry-Swift.h"
  #endif

Comment thread packages/core/ios/RNSentryInternal.swift Outdated
Comment thread packages/core/ios/RNSentry.mm
@alwx alwx requested a review from antonis July 6, 2026 09:59
Comment thread packages/core/ios/RNSentry.mm
Comment thread packages/core/ios/RNSentryInternal.swift

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2cb3360. Configure here.

Comment thread dev-packages/e2e-tests/patch-scripts/rn.patch.podfile.js Outdated
Comment thread packages/core/ios/RNSentryStart.m
Comment thread packages/core/ios/RNSentryInternal.swift
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 432.43 ms 481.57 ms 49.14 ms
Size 49.74 MiB 55.09 MiB 5.35 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
eb93136+dirty 416.18 ms 467.32 ms 51.14 ms
ae37560+dirty 470.40 ms 564.12 ms 93.72 ms
5b7e8a7+dirty 418.55 ms 488.46 ms 69.91 ms
c823bb5+dirty 409.87 ms 478.57 ms 68.70 ms
3817909+dirty 406.67 ms 416.58 ms 9.91 ms
bc0d8cf+dirty 412.37 ms 466.26 ms 53.89 ms
5a23c47+dirty 423.85 ms 466.47 ms 42.62 ms
0a147b2+dirty 464.71 ms 538.81 ms 74.09 ms
a50b33d+dirty 500.81 ms 532.11 ms 31.30 ms
7887847+dirty 416.61 ms 462.04 ms 45.43 ms

App size

Revision Plain With Sentry Diff
eb93136+dirty 48.30 MiB 53.58 MiB 5.28 MiB
ae37560+dirty 48.30 MiB 53.60 MiB 5.29 MiB
5b7e8a7+dirty 48.30 MiB 53.58 MiB 5.28 MiB
c823bb5+dirty 48.30 MiB 53.58 MiB 5.28 MiB
3817909+dirty 43.75 MiB 48.08 MiB 4.33 MiB
bc0d8cf+dirty 48.30 MiB 53.48 MiB 5.18 MiB
5a23c47+dirty 49.74 MiB 54.82 MiB 5.07 MiB
0a147b2+dirty 49.74 MiB 55.08 MiB 5.34 MiB
a50b33d+dirty 43.75 MiB 48.08 MiB 4.33 MiB
7887847+dirty 49.74 MiB 54.81 MiB 5.07 MiB

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Android (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 447.06 ms 515.21 ms 68.15 ms
Size 49.74 MiB 55.09 MiB 5.35 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
1122a96+dirty 510.16 ms 542.00 ms 31.84 ms
7ac3378+dirty 410.67 ms 442.60 ms 31.92 ms
b04af96+dirty 430.33 ms 485.98 ms 55.65 ms
3817909+dirty 357.52 ms 391.52 ms 34.00 ms
20fbd51+dirty 594.38 ms 655.35 ms 60.97 ms
0a147b2+dirty 442.80 ms 522.24 ms 79.44 ms
a5d243c+dirty 499.20 ms 525.62 ms 26.42 ms
2363c0f+dirty 419.48 ms 463.92 ms 44.44 ms
5257d80+dirty 472.37 ms 494.54 ms 22.17 ms
0a9e622+dirty 425.22 ms 457.00 ms 31.78 ms

App size

Revision Plain With Sentry Diff
1122a96+dirty 48.30 MiB 53.54 MiB 5.24 MiB
7ac3378+dirty 43.94 MiB 48.99 MiB 5.05 MiB
b04af96+dirty 49.74 MiB 55.00 MiB 5.26 MiB
3817909+dirty 43.94 MiB 48.94 MiB 5.00 MiB
20fbd51+dirty 49.74 MiB 54.81 MiB 5.07 MiB
0a147b2+dirty 49.74 MiB 55.08 MiB 5.34 MiB
a5d243c+dirty 48.30 MiB 53.54 MiB 5.23 MiB
2363c0f+dirty 49.74 MiB 54.84 MiB 5.10 MiB
5257d80+dirty 48.30 MiB 53.58 MiB 5.28 MiB
0a9e622+dirty 49.74 MiB 55.09 MiB 5.34 MiB

Comment thread CHANGELOG.md Outdated
Comment thread CHANGELOG.md Outdated
@alwx alwx force-pushed the alwx/refactor/migrate-private-sentry-sdk-only branch from b5e317f to 67f363b Compare July 7, 2026 07:53
@alwx alwx requested a review from antonis July 7, 2026 07:56
alwx and others added 2 commits July 7, 2026 10:41
Replace the deprecated PrivateSentrySDKOnly SPI and SentrySwizzle.h macro
with the new SentrySDK.internal Swift API exposed by sentry-cocoa 9.19.0,
via a thin in-pod ObjC<->Swift bridge (RNSentryInternal.swift). Imports
Sentry with @_spi(Private) for SPI-gated sub-APIs (performance, replay,
envelope). Drops the performSelector workaround around getReplayIntegration
since SentrySDK.internal.replay.capture() returns Bool directly.

Closes #6370

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move RNSentryStartTests, RNSentryStartFromFileTests, and RNSentryReplayOptionsTests
to SentrySDK.internal.{options, options(fromDictionary:), appStart.hybridSDKMode,
performance.framesTrackingHybridSDKMode}.

The ObjC RNSentryTests.m keeps PrivateSentrySDKOnly for now — the test target
is a separate consumer of the RNSentry static-lib pod and cannot see
RNSentry-Swift.h without modulemap surgery. Worth revisiting when sentry-cocoa
removes PrivateSentrySDKOnly in the next major.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
alwx and others added 11 commits July 7, 2026 10:41
`SentryInternalSwizzleApi` in sentry-cocoa has no platform gating, and
`SENTRY_HAS_UIKIT` (which guards the `[RNSentryRNSScreen swizzleViewDidAppear]`
call in RNSentry.mm) covers visionOS. The Swift bridge was gated to
`iOS/tvOS/macCatalyst` only, silently no-oping the swizzle on visionOS
where the deprecated `SentrySwizzle` macro used to work.

Also documents why `setCurrentScreen` remains a visionOS no-op: sentry-cocoa's
`SentryInternalScreenApi` is intentionally gated to iOS/tvOS, so the new API
surface itself doesn't expose the setter on visionOS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`withUnsafePointer(to:)` only guarantees the pointer stays valid inside
the closure body. Capturing it in `keyPtr` and using it outside the
closure was undefined behaviour by contract (even though `static var`
storage happens to be stable in practice). Wrap the entire
`instanceMethod` call inside `withUnsafePointer` so the pointer is
always used within its documented lifetime; the `static var` backing
storage keeps the address itself stable across calls, so sentry-cocoa's
`oncePerClass` dedup keeps working.

Flagged by Cursor Bugbot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Handle both static-lib and framework-style Pod integrations by falling
back from `<RNSentry/RNSentry-Swift.h>` (frameworks path) to the local
`"RNSentry-Swift.h"` (static-lib path) via `__has_include`. Applied to
every `.m`/`.mm` in packages/core/ios that consumes the Swift bridge.

Suggested by @antonis.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two fixes flagged by Warden:

1. `RNSentryInternal.envelope(fromData:)` accepted a non-optional Swift
   `Data`. If the ObjC caller passed a nil `NSData*` (e.g. from a failed
   base64 decode of the envelope payload), the ObjC→Swift bridge would
   force-unwrap it and crash before the method body ran. Change the
   parameter to `Data?` with an internal `guard let` — matches the
   nil-tolerant behaviour of the deprecated
   `PrivateSentrySDKOnly.envelopeWithData:`.

2. Swift Cocoa tests accessed `SentrySDK.internal.*` with plain
   `import Sentry`. The `.internal` accessor is public today, but the
   sub-APIs the tests touch (`options`, `appStart`, `performance`) sit
   next to `@_spi(Private)`-gated siblings on the same struct. Add
   `@_spi(Private) import Sentry` to `RNSentryStartTests.swift`,
   `RNSentryStartFromFileTests.swift`, and `RNSentryReplayOptionsTests.swift`
   so they keep compiling if the surface is ever tightened.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When `captureViewHierarchy` returned nil the ObjC code still constructed
an empty `NSMutableArray` and resolved with it. In JS, `[]` is truthy —
the caller would treat the failure as a successful (empty) attachment.
Bail out with `resolve(nil)` up front so the JS side sees the actual
failure signal.

Pre-existing since 2023; flagged by Seer while reviewing this PR.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cover the bridge's own contract — nil-guard on `envelope(fromData:)`,
metadata accessors, options factory, hybrid-SDK-mode flags, and the
`swizzleRNSScreenViewDidAppear` early-return when RNSScreen is
unavailable. sentry-cocoa owns the underlying `SentrySDK.internal.*`
behaviour; we just assert the wrapper forwards correctly and honours
the documented nil / platform guards.

Registers the new file in the RNSentryCocoaTester project explicitly
(the target does not use file-system-synchronized groups yet).

Flagged by Warden.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adding Swift to the RNSentry pod means CocoaPods refuses to integrate it
against non-modular ObjC dependencies. On older React Native versions
(e.g. 0.71 with Hermes) the React-hermes pod does not define modules, so
`pod install` fails with:

  The Swift pod `RNSentry` depends upon `React-hermes`, which does not
  define modules.

Inject `use_modular_headers!` into the Podfile via the existing e2e patch
script, and document the same knob for downstream users in the CHANGELOG.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previous version unconditionally set `modularPatched = true` inside the
`use_modular_headers!` branch, so a Podfile without a
`prepare_react_native_project!` anchor would be reported as successfully
patched even though `content.replace` returned it unchanged.

Compare replace output against the input and log a warning instead when
the anchor is missing.

Flagged by Cursor Bugbot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The migration to `SentrySDK.internal.screen.setCurrent` drops the
current-screen breadcrumb enrichment on visionOS because sentry-cocoa's
new `SentryInternalScreenApi` is gated to iOS/tvOS only. Already
documented in a code comment; call it out for downstream users too.

Flagged by Seer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`RNSentryInternal` is a Swift type compiled into the `RNSentry` pod
module, not the `Sentry` module. Without `@_spi(Private) import RNSentry`
the test target cannot resolve `RNSentryInternal.*` and fails to build.

Flagged by Warden.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Antonis review nit: the use_modular_headers! requirement is a
user-facing Podfile change on older RN versions, not internal-only —
promote it from ### Internal into ### Changes alongside the visionOS
setCurrentScreen note. Also reorder the Unreleased sections to the
usual Features / Changes / Fixes / Internal layout used elsewhere in
this file.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@alwx alwx force-pushed the alwx/refactor/migrate-private-sentry-sdk-only branch from 67f363b to 23f1729 Compare July 7, 2026 08:42
@alwx alwx self-assigned this Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Triggers the full CI test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate from PrivateSentrySDKOnly to [SentryObjCSDK internal]

2 participants