Skip to content

fix(ios): Force-load the Sentry static archive to keep ObjC category methods - #6615

Merged
antonis merged 7 commits into
mainfrom
fix/ios-force-load-sentry-xcframework
Aug 28, 2026
Merged

fix(ios): Force-load the Sentry static archive to keep ObjC category methods#6615
antonis merged 7 commits into
mainfrom
fix/ios-force-load-sentry-xcframework

Conversation

@antonis

@antonis antonis commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Bugfix

📜 Description

RN consumes sentry-cocoa as a prebuilt static Sentry.xcframework, linked implicitly via @import Sentry autolink with no whole-archive linker flag. Objective-C category methods — which is what Swift @objc extensions compile to — are dead-stripped from a static library at link time unless the consumer opts in. When cocoa 9.25/9.26 moved SentryReplayNetworkDetails' capture methods into such an extension (SentryReplayNetworkDetails+Capture), the selector went missing at runtime and crashed with -[Sentry.SentryReplayNetworkDetails setRequestWithSize:bodyData:contentType:allHeaders:configuredHeaders:]: unrecognized selector.

This adds a per-SDK -force_load of the Sentry binary, placed on whichever target's link actually performs the strip — which depends on linkage:

  • Static (the RN default): RNSentry and the Sentry archive both land in the app binary, so the strip happens at the app link → flag on user_target_xcconfig.
  • Dynamic (use_frameworks! :linkage => :dynamic): @import Sentry pulls the static archive into the RNSentry dylib, so the strip happens at the RNSentry link → flag on pod_target_xcconfig.

It's placed on exactly one target, never both, which avoids forcing a second copy of Sentry into the app binary (duplicate ObjC classes / two SentrySDK singletons). Linkage is detected via ENV['USE_FRAMEWORKS'], the convention RN's Podfile setup and our samples already use.

-force_load is chosen over -ObjC/-all_load because it is scoped to the Sentry archive (no whole-app size bloat or duplicate-symbol risk) and, unlike -ObjC, also retains Swift-only metadata — so it hardens against future valid-but-strippable upstream changes, not only today's category case.

Note

Skipping changelog since the bug exposed with the Cocoa bump has not been shipped

💡 Motivation and Context

Fixes the iOS Session Replay network-capture crash reported in #6609. Apple QA1490 documents category stripping from static libraries as the consumer's responsibility to resolve at link time. This is a durable consumer-side fix independent of any producer-side patch in sentry-cocoa (getsentry/sentry-cocoa#8872).

💚 How did you test it?

  • Multiple CI build configurations
  • Linked a minimal ObjC harness the same way RN links Sentry (static, no whole-archive flag) against the unpatched buggy 9.26.0 simulator slice: without -force_load the selector is absent (crash reproduced); with the exact flag the podspec now emits, the selector is present. Verified manually via a local pod install + sample-app build.
  • Ran a real pod install on the RN sample in both linkage modes and confirmed -force_load lands on exactly the right target: static → app aggregate (present) / RNSentry pod (absent); dynamic → app aggregate (absent) / RNSentry pod (present).

📝 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.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.
  • No breaking changes.

🔮 Next steps

…methods

RN links sentry-cocoa as a prebuilt static xcframework with no whole-archive
linker flag. Objective-C category methods (Swift `@objc extension`s, e.g.
`SentryReplayNetworkDetails+Capture` in cocoa 9.25/9.26) are therefore
dead-stripped at the app's final link and crash at runtime with
`unrecognized selector`.

Add per-SDK `-force_load` of the Sentry binary to `user_target_xcconfig` (the
app target, where the stripping link happens). Scoped to the Sentry archive —
unlike whole-link `-ObjC`/`-all_load` — and, unlike `-ObjC`, also retains
Swift-only metadata, so it hardens against future valid-but-strippable
upstream changes rather than only the current category case.

Apple QA1490 documents category stripping as the consumer's responsibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@antonis
antonis force-pushed the fix/ios-force-load-sentry-xcframework branch from de088e6 to 1d2ca67 Compare August 26, 2026 12:14
@github-actions

github-actions Bot commented Aug 26, 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).


  • fix(ios): Force-load the Sentry static archive to keep ObjC category methods by antonis in #6615
  • feat(core): Forward feature flag evaluations to the native SDKs by antonis in #6613
  • chore(deps): update Android SDK to v8.54.0 by github-actions in #6624
  • chore(deps): update Sentry Android Gradle Plugin to v6.20.0 by github-actions in #6627

🤖 This preview updates automatically when you update the PR.

@antonis antonis added the ready-to-merge Triggers the full CI test suite label Aug 26, 2026
@sentry

sentry Bot commented Aug 26, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

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

⚙️ sentry-react-native Build Distribution Settings

Comment thread packages/core/RNSentry.podspec Outdated
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3839.40 ms 1220.56 ms -2618.84 ms
Size 5.08 MiB 6.75 MiB 1.67 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
61cc206+dirty 3822.60 ms 1206.17 ms -2616.43 ms
b9bebee+dirty 3858.02 ms 1231.92 ms -2626.11 ms
2363c0f+dirty 3834.61 ms 1224.46 ms -2610.15 ms
038a6d7+dirty 3838.96 ms 1218.86 ms -2620.10 ms
7ff4d0f+dirty 3859.87 ms 1224.75 ms -2635.12 ms
d7d54c6+dirty 3842.44 ms 1204.87 ms -2637.57 ms
1122a96+dirty 3839.17 ms 1219.23 ms -2619.93 ms
23598c3+dirty 1223.59 ms 1229.13 ms 5.53 ms
af33f3b+dirty 3851.28 ms 1233.06 ms -2618.21 ms
5a316ea+dirty 3831.56 ms 1214.31 ms -2617.26 ms

App size

Revision Plain With Sentry Diff
61cc206+dirty 4.98 MiB 6.55 MiB 1.57 MiB
b9bebee+dirty 5.15 MiB 6.68 MiB 1.53 MiB
2363c0f+dirty 4.98 MiB 6.46 MiB 1.49 MiB
038a6d7+dirty 5.15 MiB 6.70 MiB 1.55 MiB
7ff4d0f+dirty 5.15 MiB 6.70 MiB 1.55 MiB
d7d54c6+dirty 4.98 MiB 6.51 MiB 1.53 MiB
1122a96+dirty 5.15 MiB 6.68 MiB 1.53 MiB
23598c3+dirty 3.38 MiB 4.80 MiB 1.42 MiB
af33f3b+dirty 4.98 MiB 6.51 MiB 1.54 MiB
5a316ea+dirty 4.98 MiB 6.51 MiB 1.53 MiB

Previous results on branch: fix/ios-force-load-sentry-xcframework

Startup times

Revision Plain With Sentry Diff
cb0f8ba+dirty 3854.25 ms 1217.10 ms -2637.15 ms
407f7ca+dirty 3821.19 ms 1220.00 ms -2601.19 ms
3388a65+dirty 3845.02 ms 1217.58 ms -2627.44 ms
130342e+dirty 3848.04 ms 1222.47 ms -2625.57 ms
aa762be+dirty 3843.60 ms 1221.47 ms -2622.13 ms
3cfd253+dirty 3835.00 ms 1222.95 ms -2612.05 ms

App size

Revision Plain With Sentry Diff
cb0f8ba+dirty 5.08 MiB 6.78 MiB 1.71 MiB
407f7ca+dirty 5.08 MiB 6.75 MiB 1.67 MiB
3388a65+dirty 5.08 MiB 6.78 MiB 1.70 MiB
130342e+dirty 5.08 MiB 6.75 MiB 1.67 MiB
aa762be+dirty 5.08 MiB 6.78 MiB 1.70 MiB
3cfd253+dirty 5.08 MiB 6.78 MiB 1.70 MiB

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3852.40 ms 1223.64 ms -2628.76 ms
Size 5.08 MiB 6.75 MiB 1.67 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
ecf47a2+dirty 3835.20 ms 1222.43 ms -2612.77 ms
3817909+dirty 1183.90 ms 1187.50 ms 3.60 ms
5ee78d6+dirty 3847.52 ms 1224.27 ms -2623.25 ms
890d145+dirty 1223.59 ms 1231.37 ms 7.78 ms
e763471+dirty 3841.16 ms 1218.06 ms -2623.09 ms
3d31fcf+dirty 3838.09 ms 1223.46 ms -2614.63 ms
9210ae6+dirty 3815.93 ms 1214.14 ms -2601.79 ms
26843eb+dirty 3861.60 ms 1238.77 ms -2622.82 ms
7a89652+dirty 3861.46 ms 1229.61 ms -2631.85 ms
9ad4522+dirty 3850.40 ms 1222.40 ms -2628.00 ms

App size

Revision Plain With Sentry Diff
ecf47a2+dirty 4.98 MiB 6.46 MiB 1.49 MiB
3817909+dirty 3.38 MiB 4.73 MiB 1.35 MiB
5ee78d6+dirty 5.15 MiB 6.69 MiB 1.53 MiB
890d145+dirty 3.38 MiB 4.77 MiB 1.38 MiB
e763471+dirty 4.98 MiB 6.51 MiB 1.53 MiB
3d31fcf+dirty 4.98 MiB 6.56 MiB 1.58 MiB
9210ae6+dirty 5.15 MiB 6.68 MiB 1.53 MiB
26843eb+dirty 4.98 MiB 6.53 MiB 1.55 MiB
7a89652+dirty 5.15 MiB 6.70 MiB 1.55 MiB
9ad4522+dirty 4.98 MiB 6.56 MiB 1.58 MiB

Previous results on branch: fix/ios-force-load-sentry-xcframework

Startup times

Revision Plain With Sentry Diff
cb0f8ba+dirty 3854.67 ms 1230.67 ms -2624.00 ms
407f7ca+dirty 3851.39 ms 1217.18 ms -2634.20 ms
130342e+dirty 3850.30 ms 1214.04 ms -2636.25 ms
aa762be+dirty 3864.52 ms 1218.87 ms -2645.65 ms

App size

Revision Plain With Sentry Diff
cb0f8ba+dirty 5.08 MiB 6.78 MiB 1.71 MiB
407f7ca+dirty 5.08 MiB 6.75 MiB 1.67 MiB
130342e+dirty 5.08 MiB 6.75 MiB 1.67 MiB
aa762be+dirty 5.08 MiB 6.78 MiB 1.70 MiB

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

✅ 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 1ec02c1. Configure here.

@antonis
antonis force-pushed the fix/ios-force-load-sentry-xcframework branch 2 times, most recently from 139ed15 to 288b362 Compare August 26, 2026 13:20
Under `use_frameworks! :linkage => :dynamic`, `@import Sentry` pulls the
static Sentry archive into the RNSentry dylib, so RNSentry's own link is
what dead-strips the `SentryReplayNetworkDetails+Capture` ObjC category —
not the app link. Placing `-force_load` only on `user_target_xcconfig`
missed that link, and additionally risked forcing a second copy of Sentry
into the app binary (duplicate ObjC classes, two SentrySDK singletons).

Detect linkage via `ENV['USE_FRAMEWORKS']` (the signal RN's Podfile setup
and our samples already use) and put `-force_load` on exactly one target:
`pod_target_xcconfig` (RNSentry dylib link) for dynamic, otherwise
`user_target_xcconfig` (app link) for the static default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@antonis
antonis force-pushed the fix/ios-force-load-sentry-xcframework branch from 288b362 to 8c1a078 Compare August 26, 2026 13:21
@antonis
antonis marked this pull request as ready for review August 26, 2026 13:23
Comment thread packages/core/RNSentry.podspec
# `ENV['USE_FRAMEWORKS']`.
force_load_flags = SENTRY_XCFRAMEWORK_SLICES_BY_SDK.each_with_object({}) do |(sdk, slice_ids), acc|
loads = slice_ids.map do |slice|
%(-force_load "#{File.join(sentry_xcframework_ref, slice, 'Sentry.framework', 'Sentry')}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

m: why force_load over ObjC? I believe force load will disable dead code stripping for all members in the archive, where ObjC would only do that for Objective-C members. In reality it probably doesn't matter all that much, but force_load could increase binary size more than needed.

@antonis antonis Aug 27, 2026

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.

Good point! force_load would probably future proof against wider swift refactorings in the cocoa sdk but ObjC should be enough for this bug because SentryReplayNetworkDetails+Capture compiles to an ObjC category.
Trying this out with dc780c5

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! My thinking was that using force_load will include all non-ObjC members too (i.e. C/C++) that might otherwise be stripped if not used - and thus inflate the binary unnecessarily. Since we're only calling it on Sentry frameworks it's probably not the end of the world - but when we have KSCrash installed we might see things being pulled in that we don't need.

@antonis antonis Aug 27, 2026

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.

Trying this out with dc780c5

The CI is failing with the same issue that started this investigation :(

I'll revert back to the force_load solution and iterate on the warden warning

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Huh... weird. Okay thanks for trying it! LGTM

antonis and others added 2 commits August 27, 2026 11:24
Switch the link-time fix for the stripped `SentryReplayNetworkDetails`
ObjC category from `-force_load <Sentry binary>` to `-ObjC`:

- `-ObjC` names no archive, so it only affects static libs actually in the
  current link — the same flag is correct on both the app target (static
  linkage) and the RNSentry pod target (dynamic linkage) with no risk of
  pulling a second copy of Sentry into the app. This removes the need to
  detect linkage via `ENV['USE_FRAMEWORKS']`, closing the gap where a bare
  `use_frameworks!` (env var unset) mis-placed the flag.
- It is Apple's documented fix for this exact problem (QA1490).
- It only retains ObjC class/category objects (not the whole archive) and
  needs no per-slice binary paths, simplifying the podspec.

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

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

Reviewed by Cursor Bugbot for commit f12d6e2. Configure here.

Comment thread packages/core/RNSentry.podspec Outdated
The -ObjC swap (dc780c5) regressed the "Test ios production REV2" job:
captureSpaceflightNewsScreenTransaction fails with the #6609 crash ("No
visible element found: Load More Articles") because the app crashes on the
replay network-detail path. -force_load (8c1a078) passes the same test
(19/19). Verified directly from CI logs of both runs.

Root cause confirmed against the checksum-verified released 9.26.0 slice:
-setRequestWithSize:bodyData:contentType:allHeaders:configuredHeaders: lives
only in the category-only object SentryReplayNetworkDetails+Capture.o (no
class of its own), so a plain static link dead-strips it. -force_load loads
the whole archive unconditionally; -ObjC does not rescue it in the real
production build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
antonis added a commit that referenced this pull request Aug 27, 2026
Revert the sentry-cocoa bump from #6409. sentry-cocoa 9.25/9.26 moved the
replay network-detail capture methods into a Swift `@objc extension`, which
compiles to an ObjC category that the prebuilt static `Sentry.xcframework`
dead-strips under the default static link, crashing at runtime with
"unrecognized selector" (#6609).

9.24.0 predates that change, so the crashing category does not exist and no
consumer-side linker workaround is needed. Roll back to it for this release;
the 9.26 upgrade plus the `-force_load` link fix can land together as a
proper follow-up (#6615).

9.26.0 was never released (Unreleased section only) and 8.23.0 already ships
9.24.0, so no user-facing changelog entry is needed — the Unreleased "Bump
Cocoa SDK" line is dropped rather than replaced with a downgrade note.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@lucas-zimerman lucas-zimerman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!

@antonis
antonis merged commit 9b3b757 into main Aug 28, 2026
87 checks passed
@antonis
antonis deleted the fix/ios-force-load-sentry-xcframework branch August 28, 2026 08:25
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 skip-changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iOS: Session Replay network-detail capture breaks requests on sentry-cocoa 9.25.0

3 participants