feat(replay): Populate trace_ids in mobile replay events - #6786
Merged
Merged
Conversation
Forward the trace id of each sent event to the native Session Replay so the current segment carries it under `trace_ids`, making replays searchable by trace id in Explore. The native SDKs (Cocoa 9.29.1, Java 8.58.0) own dedup, the 100-per-segment cap, and the no-op when no replay is recording; the RN side is a thin forward across the bridge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog.
🤖 This preview updates automatically when you update the PR. |
Contributor
|
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit b25fdd3. Configure here.
Contributor
Android (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5789645+dirty | 426.82 ms | 495.42 ms | 68.60 ms |
| fa21fca+dirty | 453.80 ms | 468.46 ms | 14.66 ms |
| a216cb9+dirty | 458.66 ms | 531.47 ms | 72.81 ms |
| 0307fa4+dirty | 454.16 ms | 518.35 ms | 64.18 ms |
| 5569641+dirty | 406.43 ms | 428.51 ms | 22.08 ms |
| 6a3eb4c+dirty | 430.90 ms | 489.98 ms | 59.08 ms |
| 5fe1c6c+dirty | 401.62 ms | 445.28 ms | 43.66 ms |
| a636fa4+dirty | 486.70 ms | 508.53 ms | 21.83 ms |
| 15d4514+dirty | 406.77 ms | 428.06 ms | 21.29 ms |
| 1e5d96d+dirty | 519.43 ms | 543.62 ms | 24.19 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5789645+dirty | 49.74 MiB | 54.85 MiB | 5.11 MiB |
| fa21fca+dirty | 49.74 MiB | 55.37 MiB | 5.63 MiB |
| a216cb9+dirty | 49.74 MiB | 55.08 MiB | 5.34 MiB |
| 0307fa4+dirty | 49.74 MiB | 54.81 MiB | 5.07 MiB |
| 5569641+dirty | 48.30 MiB | 53.48 MiB | 5.18 MiB |
| 6a3eb4c+dirty | 49.74 MiB | 55.44 MiB | 5.70 MiB |
| 5fe1c6c+dirty | 43.75 MiB | 48.14 MiB | 4.39 MiB |
| a636fa4+dirty | 49.74 MiB | 55.36 MiB | 5.62 MiB |
| 15d4514+dirty | 48.30 MiB | 53.60 MiB | 5.30 MiB |
| 1e5d96d+dirty | 49.74 MiB | 54.81 MiB | 5.07 MiB |
📲 Install BuildsAndroid
|
Contributor
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 64630e5+dirty | 3842.70 ms | 1218.11 ms | -2624.60 ms |
| b0d3373+dirty | 3831.75 ms | 1227.29 ms | -2604.46 ms |
| b04af96+dirty | 3818.92 ms | 1219.76 ms | -2599.16 ms |
| 3d31fcf+dirty | 3838.09 ms | 1223.46 ms | -2614.63 ms |
| a0a3177+dirty | 3844.73 ms | 1225.23 ms | -2619.51 ms |
| af33f3b+dirty | 3849.98 ms | 1236.45 ms | -2613.53 ms |
| 09a902f+dirty | 3835.67 ms | 1217.11 ms | -2618.57 ms |
| 5a316ea+dirty | 3820.11 ms | 1211.28 ms | -2608.83 ms |
| acd838e+dirty | 3849.78 ms | 1230.00 ms | -2619.78 ms |
| c2e182c+dirty | 3848.40 ms | 1211.79 ms | -2636.61 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 64630e5+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| b0d3373+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| b04af96+dirty | 4.98 MiB | 6.54 MiB | 1.56 MiB |
| 3d31fcf+dirty | 4.98 MiB | 6.56 MiB | 1.58 MiB |
| a0a3177+dirty | 4.98 MiB | 6.55 MiB | 1.58 MiB |
| af33f3b+dirty | 4.98 MiB | 6.51 MiB | 1.54 MiB |
| 09a902f+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| 5a316ea+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| acd838e+dirty | 5.15 MiB | 6.70 MiB | 1.55 MiB |
| c2e182c+dirty | 4.98 MiB | 6.50 MiB | 1.52 MiB |
Contributor
Android (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| bf168a4+dirty | 430.60 ms | 459.31 ms | 28.71 ms |
| 0bd8916+dirty | 400.15 ms | 442.72 ms | 42.57 ms |
| a2585ce+dirty | 414.04 ms | 456.83 ms | 42.79 ms |
| 9c84b9a+dirty | 429.26 ms | 448.90 ms | 19.64 ms |
| bc0d8cf+dirty | 407.66 ms | 461.35 ms | 53.69 ms |
| a736b76+dirty | 405.78 ms | 458.74 ms | 52.96 ms |
| 1122a96+dirty | 510.16 ms | 542.00 ms | 31.84 ms |
| 267d3ed+dirty | 424.69 ms | 483.70 ms | 59.01 ms |
| 6177334+dirty | 404.80 ms | 456.74 ms | 51.94 ms |
| 7887847+dirty | 420.47 ms | 460.55 ms | 40.08 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| bf168a4+dirty | 49.74 MiB | 55.09 MiB | 5.35 MiB |
| 0bd8916+dirty | 48.30 MiB | 53.57 MiB | 5.26 MiB |
| a2585ce+dirty | 49.74 MiB | 55.36 MiB | 5.61 MiB |
| 9c84b9a+dirty | 49.74 MiB | 55.36 MiB | 5.62 MiB |
| bc0d8cf+dirty | 48.30 MiB | 53.48 MiB | 5.18 MiB |
| a736b76+dirty | 48.30 MiB | 53.48 MiB | 5.18 MiB |
| 1122a96+dirty | 48.30 MiB | 53.54 MiB | 5.24 MiB |
| 267d3ed+dirty | 48.30 MiB | 53.58 MiB | 5.28 MiB |
| 6177334+dirty | 48.30 MiB | 53.54 MiB | 5.23 MiB |
| 7887847+dirty | 49.74 MiB | 54.81 MiB | 5.07 MiB |
Contributor
iOS (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 64630e5+dirty | 3845.49 ms | 1215.19 ms | -2630.30 ms |
| b0d3373+dirty | 3842.49 ms | 1218.49 ms | -2624.00 ms |
| b04af96+dirty | 3830.54 ms | 1206.11 ms | -2624.44 ms |
| f9c1ed4+dirty | 3842.09 ms | 1220.70 ms | -2621.40 ms |
| 09a902f+dirty | 3847.65 ms | 1221.31 ms | -2626.34 ms |
| 44abcc2+dirty | 3841.42 ms | 1214.77 ms | -2626.65 ms |
| acd838e+dirty | 3835.94 ms | 1215.87 ms | -2620.07 ms |
| bfba737+dirty | 3834.18 ms | 1222.80 ms | -2611.38 ms |
| ce7b368+dirty | 3851.41 ms | 1222.37 ms | -2629.04 ms |
| 4e0ba9c+dirty | 3856.39 ms | 1234.44 ms | -2621.95 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 64630e5+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| b0d3373+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| b04af96+dirty | 4.98 MiB | 6.54 MiB | 1.56 MiB |
| f9c1ed4+dirty | 4.98 MiB | 6.50 MiB | 1.53 MiB |
| 09a902f+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| 44abcc2+dirty | 4.98 MiB | 6.55 MiB | 1.57 MiB |
| acd838e+dirty | 5.15 MiB | 6.70 MiB | 1.55 MiB |
| bfba737+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| ce7b368+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| 4e0ba9c+dirty | 5.15 MiB | 6.67 MiB | 1.51 MiB |
antonis
marked this pull request as ready for review
September 25, 2026 09:18
alwx
added a commit
that referenced
this pull request
Sep 28, 2026
#6786 added registerReplayTraceId on main while this branch was moving the .mm callers off the Swift module. git merged both cleanly, but the result calls RNSentryInternal directly from RNSentry.mm, which no longer imports the generated Swift header -- breaking every iOS build, CocoaPods included. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
alwx
added a commit
that referenced
this pull request
Sep 30, 2026
* feat(ios): Support SwiftPM autolinking
React Native 0.87 added an opt-in Swift Package Manager integration, and
`npx react-native spm` refuses to set up an app whose autolinked library
ships no `Package.swift`. Add one, so the SDK can be consumed that way.
Three SwiftPM constraints shape the layout:
* No mixed-language targets, so the Swift sources move to `ios/Swift/`
and build as their own `RNSentrySwift` target. `.m` callers reach it
through `ios/RNSentrySwiftBridge.h`, which picks the pod's or the
package's generated header.
* `@import` is rejected in Objective-C++ and enabling C++ modules breaks
React Native's C++ headers, so `.mm` callers go through the new
`RNSentryInternalWrapper`, a plain Objective-C forwarder.
* No header maps, so the public headers are mirrored under
`ios/include/RNSentry/` (the target's `publicHeadersPath`) to keep
`#import <RNSentry/RNSentrySDK.h>` resolving. The podspec excludes the
mirrors.
The autolinked target name is pinned to `RNSentry` in both places React
Native reads it, since the name derived from `@sentry/react-native`
collides with React Native's reserved `ReactNative` and is also the
prefix consumers import our headers under.
CocoaPods is unaffected and stays the default.
* fix(ios): Register replay masks via codegen provider
`codegenConfig` declared no `ios.componentProvider`, so
`RCTThirdPartyComponentsProvider` carried no entry for
`RNSentryReplayMask` and `RNSentryReplayUnmask`. React Native then
resolved them through the legacy view manager interop layer, which 0.87
lets an app turn off with `RCT_REMOVE_LEGACY_COMPONENT_INTEROP` — and
without it the components fall back to `UnimplementedView`, which leaves
nothing for sentry-cocoa to redact, since it masks by view class.
Map both component names to their view classes, the same way other
community libraries do.
* ci: Build a SwiftPM app on iOS
Nothing covered the SwiftPM path, so an autolinking or manifest
regression would only surface in a user's project.
The app is generated per run from the React Native template instead of
committed: `react-native spm` stops on any autolinked dependency that
ships no `Package.swift`, and most community libraries still don't, so
the existing sample app cannot take this path. The job packs the SDK with
`yarn pack` (which resolves the `workspace:` ranges), installs the
tarball like a user would, and asserts that both RNSentry and
sentry-cocoa reach the app binary — a green build alone would not catch
a dependency that silently dropped out of the graph.
Also add `Package.swift` and `react-native.config.js` to the change
filters, since both drive the iOS build.
* ref(ios): Consume sentry-cocoa as a binary target
sentry-cocoa's manifest declares a binary target per distribution
variant, and SwiftPM downloads every artifact of a resolved package, not
only the ones the selected product needs. Depending on the package for
the one variant we use therefore pulled all seven archives: 2.9 GB in
DerivedData for the 339 MB we need, and seven chances for a failed
download to break the build — CI hit a GitHub 500 on
`SentryObjC-Dynamic.xcframework.zip`, which the build never uses.
Declare the `Sentry.xcframework` archive as our own binary target, the
same archive and checksum `pod install` already verifies. That leaves
one download. `update-cocoa.sh` keeps the version and the checksum in
step with the podspec.
`.linkedLibrary("c++")` replaces `SentryCppHelper`, an empty target that
sentry-cocoa pairs with the binary target to carry exactly that setting.
Reported upstream as getsentry/sentry-cocoa#9146.
* ci: Install both tarballs in the SwiftPM job
`@sentry/react-native` depends on `@sentry/expo-upload-sourcemaps` with a
`workspace:` range, which `yarn pack` rewrites to the version being
released. The job packed and installed the core tarball only, so npm
fetched that dependency from the registry — and on a `release/**` branch
the bumped version is not published yet, which fails with ETARGET before
the build even starts.
Pack both workspaces through `yarn build:tarball` and install both
tarballs, the way `buildandtest.yml` already does. `build:tarball` also
restores the executable bits that `yarn pack` drops.
Reported by Warden.
* fix(ios): Build RNSentryInternalWrapper on every platform
The wrapper mirrored `RNSentryInternal` without its platform gating, so
the macOS, tvOS and visionOS sample builds failed to compile it.
`setCurrentScreen:`, `captureScreenshots` and `captureViewHierarchy`
exist for iOS, tvOS and visionOS only — `RNSentryInternal` declares no
stubs for the other platforms — so the mirror now carries the guard the
call sites in `RNSentry.mm` already use.
`collectProfileBetween:and:forTrace:` was a second, older problem: the
watchOS/tvOS/visionOS stub was missing the explicit `@objc` selector
that its counterpart declares, so the selector differed by platform.
Nothing noticed because the only caller sits behind
`SENTRY_TARGET_PROFILING_SUPPORTED`. Declare it on the stub too.
* fix(ios): Pin sentry-cocoa 9.29.1 in Package.swift
main moved to 9.29.1 while this branch was open, so the SwiftPM path
stayed a patch behind the CocoaPods one. Take the version and the
checksum from `sentry_utils.rb`, which the CocoaPods path already
verifies — a clean resolve accepts them, which also confirms both
consumers hash the same archive.
* ci: Assert a class name, not the package name
The SwiftPM link check searched the app binary for `sentry-cocoa`. That
string is incidental to the prebuilt dependency, so an upstream change
could drop it and fail a valid build. Look for `SentryOptions` instead:
the linker keeps the name of every Objective-C class it links, so the
marker is present whenever the library is.
Reported by Warden.
* fix(ios): Sync Package.swift to sentry-cocoa 9.29.2
The 9.29.2 bump (#6785, #6787) landed on main while this branch was open
and ran update-cocoa.sh before it learned about Package.swift, so the
SwiftPM manifest still pinned 9.29.1. Checksum matches sentry-cocoa's own
Package.swift at tag 9.29.2.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(ios): Route registerReplayTraceId through RNSentryInternalWrapper
#6786 added registerReplayTraceId on main while this branch was moving the
.mm callers off the Swift module. git merged both cleanly, but the result
calls RNSentryInternal directly from RNSentry.mm, which no longer imports
the generated Swift header -- breaking every iOS build, CocoaPods included.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(changelog): Note SwiftPM support is iOS-only
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* ci(ios): Assert Sentry ObjC categories survive the SwiftPM link
The existing check greps for class names, which reach the binary even when
their categories do not. sentry-cocoa ships category-only object files
(SentryReplayNetworkDetails+Capture, Options+Dictionary,
SentryNSNotificationCenterWrapper) that nothing references, so the linker
drops them from the static archive unless it is force-loaded -- #6609.
CocoaPods uses -force_load for this; the SwiftPM path has no equivalent,
so measure whether it actually matters here.
The check confirms the canary selector still exists upstream before
treating its absence in the app binary as a failure, so a sentry-cocoa
rename warns instead of failing spuriously.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* ci(ios): Fail the category check when the Sentry archive is missing
A path or layout change made `find` return nothing, which took the same
soft-pass branch as a renamed upstream selector and skipped the assertion
entirely -- the one SwiftPM guard against the #6609 dead-strip crash.
Split the two: a missing archive fails, a stale canary still warns.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(ios): Anchor the Package.swift npmignore rule to the package root
Unanchored, `!Package.swift` re-included the file at any depth, so a
machine that had run the Cocoa tests also shipped the gitignored
`RNSentryCocoaTester/build/generated/ios/Package.swift` -- making the
published tarball depend on local build state. Anchor it like the other
root-level entries.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs: Document the SwiftPM setup in the README
The steps only existed in spm-application.yml, so trying SwiftPM meant
reading a CI workflow. Covers the two things people hit first: every
autolinked dependency needs a Package.swift, and the path is iOS only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(ios): Move the replay mask codegen fix to its own PR
The codegen componentProvider entry is independent of the SwiftPM work
and affects CocoaPods builds on React Native 0.87 the same way. It also
affects PII, so it gets its own test and revert trail. Moved to #6810.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📢 Type of change
📜 Description
The
mobileReplayIntegrationnow forwards the trace id of each sent event to the native Session Replay, so the current segment carries it undertrace_ids. This makes React Native mobile replays discoverable when searching by trace id in Explore, matching web replay and the native SDKs.Implementation is a thin forward across the JS↔native bridge:
afterSendEventstep readsevent.contexts.trace.trace_idand forwards it via a newNATIVE.registerReplayTraceId(traceId)seam. Only forwards while a replay is recording (guarded by the cached replay id); the collection logic lives in a smallreplay/replayTraceIds.tsmodule.registerReplayTraceId(traceId: string): voidon theRNSentryTurboModule spec. Degrades to a no-op on older cached native binaries (typeofguard).SentrySDK.internal.replay.registerTraceId(SentryId)viaRNSentryInternal, converting the 32-char hex trace id to aSentryId(reusing the hyphenation helper shared withsetCurrentScopePropagationContext).getReplayController().registerTraceId(new SentryId(traceId)).💡 Motivation and Context
Fixes #6232
💚 How did you test it?
📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps