Skip to content

fix(profiling): iOS UI profiling on v8#6012

Merged
antonis merged 6 commits intomainfrom
antonis/fix-ios-ui-profiling-options
Apr 23, 2026
Merged

fix(profiling): iOS UI profiling on v8#6012
antonis merged 6 commits intomainfrom
antonis/fix-ios-ui-profiling-options

Conversation

@antonis
Copy link
Copy Markdown
Contributor

@antonis antonis commented Apr 17, 2026

📢 Type of change

  • Bugfix

📜 Description

RNSentryStart.createOptionsWithDictionary — the live iOS init path reached from Sentry.init() via initNativeSdk — does not read _experiments.profilingOptions. The JS wrapper packs profilingOptions into _experiments.profilingOptions (wrapper.ts:306-312) and sends it across the bridge, but on iOS the dictionary is handed to Sentry Cocoa without the configureProfiling callback being installed. profileSessionSampleRate, lifecycle, and startOnAppStart are silently dropped.

The handling exists in SentrySDKWrapper.createOptionsWithDictionary (ios/SentrySDKWrapper.m:103-116), but that method hasn't been part of initNativeSdk's call path since #5582 moved init to RNSentryStart. #5611 then added the profiling block to SentrySDKWrapper (the already-bypassed file) a week later, so the feature has never reached Cocoa through the JS init path in any v8 release.

This PR ports the _experiments.profilingOptions handling to RNSentryStart.createOptionsWithDictionary and adds XCTest coverage — including one test against startWithOptions so the full entry point used by initNativeSdk stays pinned. The positive tests invoke the installed callback on a probe SentryProfileOptions and assert sessionSampleRate, lifecycle, and profileAppStarts all land correctly, so a future incomplete callback regresses loudly.

Scope is intentionally narrow. The same dead SentrySDKWrapper options surface also contains _experiments.enableUnhandledCPPExceptionsV2 handling that is unreached in the same way — that should be ported in a follow-up. (Top-level enableLogs in SentrySDKWrapper is redundant, not broken: Cocoa's SentryOptionsInternal.m:96 already maps it from the raw dictionary.) Cleaning up the dead SentrySDKWrapper.createOptionsWithDictionary / setupWithDictionary (and their tests) is also a good follow-up — the reason this bug shipped is precisely that those dead-code tests kept passing.

💡 Motivation and Context

iOS UI profiling is documented as available for React Native (https://docs.sentry.io/platforms/react-native/profiling/#ui-profiling-experimental), but no profilingOptions set through JS Sentry.init({ _experiments: { profilingOptions: { ... } } }) actually takes effect on iOS in any 8.x release. Users cannot opt into UI profiling via the documented path.

💚 How did you test it?

  • Added four XCTest cases under #pragma mark - RNSentryStart Tests:
    • testStartWithDictionaryInstallsConfigureProfilingFromExperimentsProfilingOptions — pins the full startWithOptions entry point used by initNativeSdk; invokes the installed callback on a probe and asserts all three fields land.
    • testStartCreateOptionsWithDictionaryProfilingOptionsInstallsConfigureProfiling — unit test for the ported block; invokes the callback on a probe and asserts all three fields.
    • testStartCreateOptionsWithDictionaryProfilingOptionsMissingDoesNotInstallConfigureProfiling + …EmptyExperimentsDoesNotInstall… — negative cases.
  • All new tests are guarded by #if SENTRY_TARGET_PROFILING_SUPPORTED matching the implementation in RNSentryExperimentalOptions.m.
  • Locally: yarn build, yarn test (210 passing), yarn circularDepCheck, ./scripts/clang-format.sh lint, yarn lint:android, yarn lint:kotlin all clean. yarn lint:oxlint has 2 pre-existing errors in sentryExpoNativeCheck.ts (unrelated; present on main). Full Cocoa XCTest run relies on CI's simulator.

📝 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

`RNSentryStart.createOptionsWithDictionary` (the live iOS init path
since v8.0.0) did not read `_experiments.profilingOptions`, silently
dropping `profileSessionSampleRate`, `lifecycle`, and `startOnAppStart`.
The handling existed in `SentrySDKWrapper`, but that surface hasn't
been called from `initNativeSdk` since #5582#5611 added the block
to the wrong file.

Port the handling to `RNSentryStart` and add XCTest coverage for the
`startWithOptions` entry point that `initNativeSdk` uses, so this
regression can't slip through again.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 17, 2026

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(profiling): iOS UI profiling on v8 by antonis in #6012
  • feat: Expose screenshot masking options for error screenshots by antonis in #6007
  • fix(replay): Check captureReplay return value in iOS bridge by antonis in #6008
  • chore(deps): bump getsentry/craft from 2.25.2 to 2.25.4 by dependabot in #6019
  • chore(deps): bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.25.2 to 2.25.4 by dependabot in #6021
  • chore(deps): bump github/codeql-action from 4.35.1 to 4.35.2 by dependabot in #6022
  • chore(deps): bump actions/setup-node from 6.3.0 to 6.4.0 by dependabot in #6020
  • ci(danger): Demote Android SDK version mismatch from fail to warn by antonis in #6018
  • chore(deps): update Android SDK to v8.39.1 by github-actions in #6010
  • chore(deps): update JavaScript SDK to v10.49.0 by github-actions in #6011
  • ci: Integrate Warden for AI-powered PR code review by antonis in #6003
  • chore(lint): Fixes lint issue on main by antonis in #6013
  • feat(expo): Warn when prebuilt native projects are missing Sentry config by alwx in #5984

🤖 This preview updates automatically when you update the PR.

@antonis
Copy link
Copy Markdown
Contributor Author

antonis commented Apr 17, 2026

@sentry review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

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 dfbadc4. Configure here.

- Rewrite CHANGELOG entry to match the descriptive + PR-link format.
- Strengthen the two positive profiling tests to invoke the installed
  callback on a fresh SentryProfileOptions probe and assert that
  sessionSampleRate, lifecycle, and profileAppStarts land correctly,
  so a future regression that installs an incomplete callback is caught.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

@antonis antonis left a comment

Choose a reason for hiding this comment

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

The linter issue should be fixed after #6013

@sentry
Copy link
Copy Markdown

sentry Bot commented Apr 17, 2026

📲 Install Builds

Android

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

⚙️ sentry-react-native Build Distribution Settings

@github-actions
Copy link
Copy Markdown
Contributor

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1201.30 ms 1204.27 ms 2.97 ms
Size 3.38 MiB 4.76 MiB 1.38 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
3817909+dirty 1210.76 ms 1215.64 ms 4.89 ms
3ce5254+dirty 1217.70 ms 1224.69 ms 6.99 ms
04207c4+dirty 1228.55 ms 1226.04 ms -2.51 ms
7ac3378+dirty 1202.35 ms 1198.31 ms -4.04 ms
5c1e987+dirty 1208.43 ms 1220.72 ms 12.29 ms
0d9949d+dirty 1203.94 ms 1202.27 ms -1.67 ms
4953e94+dirty 1217.41 ms 1223.53 ms 6.12 ms
df5d108+dirty 1207.34 ms 1210.50 ms 3.16 ms
a50b33d+dirty 1207.11 ms 1212.10 ms 5.00 ms
3d377b5+dirty 1201.55 ms 1201.80 ms 0.25 ms

App size

Revision Plain With Sentry Diff
3817909+dirty 3.38 MiB 4.73 MiB 1.35 MiB
3ce5254+dirty 3.38 MiB 4.76 MiB 1.38 MiB
04207c4+dirty 3.38 MiB 4.76 MiB 1.38 MiB
7ac3378+dirty 3.38 MiB 4.76 MiB 1.38 MiB
5c1e987+dirty 3.38 MiB 4.73 MiB 1.35 MiB
0d9949d+dirty 3.38 MiB 4.76 MiB 1.38 MiB
4953e94+dirty 3.38 MiB 4.73 MiB 1.35 MiB
df5d108+dirty 3.38 MiB 4.73 MiB 1.35 MiB
a50b33d+dirty 3.38 MiB 4.73 MiB 1.35 MiB
3d377b5+dirty 3.38 MiB 4.76 MiB 1.38 MiB

@github-actions
Copy link
Copy Markdown
Contributor

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1217.49 ms 1218.20 ms 0.71 ms
Size 3.38 MiB 4.76 MiB 1.38 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
3817909+dirty 1183.90 ms 1187.50 ms 3.60 ms
3ce5254+dirty 1219.93 ms 1221.90 ms 1.96 ms
04207c4+dirty 1191.27 ms 1189.78 ms -1.48 ms
7ac3378+dirty 1213.37 ms 1218.15 ms 4.78 ms
5c1e987+dirty 1204.30 ms 1222.15 ms 17.85 ms
0d9949d+dirty 1211.38 ms 1219.67 ms 8.29 ms
4953e94+dirty 1212.06 ms 1214.83 ms 2.77 ms
df5d108+dirty 1225.90 ms 1220.14 ms -5.76 ms
a50b33d+dirty 1197.74 ms 1197.17 ms -0.57 ms
3d377b5+dirty 1218.48 ms 1219.51 ms 1.03 ms

App size

Revision Plain With Sentry Diff
3817909+dirty 3.38 MiB 4.73 MiB 1.35 MiB
3ce5254+dirty 3.38 MiB 4.76 MiB 1.38 MiB
04207c4+dirty 3.38 MiB 4.76 MiB 1.38 MiB
7ac3378+dirty 3.38 MiB 4.76 MiB 1.38 MiB
5c1e987+dirty 3.38 MiB 4.73 MiB 1.35 MiB
0d9949d+dirty 3.38 MiB 4.76 MiB 1.38 MiB
4953e94+dirty 3.38 MiB 4.73 MiB 1.35 MiB
df5d108+dirty 3.38 MiB 4.73 MiB 1.35 MiB
a50b33d+dirty 3.38 MiB 4.73 MiB 1.35 MiB
3d377b5+dirty 3.38 MiB 4.76 MiB 1.38 MiB

@antonis antonis removed the ready-to-merge Triggers the full CI test suite label Apr 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Fails
🚫 Pull request is not ready for merge, please add the "ready-to-merge" label to the pull request

Generated by 🚫 dangerJS against 6484ed7

Copy link
Copy Markdown
Collaborator

@lucas-zimerman lucas-zimerman left a comment

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 0589692 into main Apr 23, 2026
56 of 62 checks passed
@antonis antonis deleted the antonis/fix-ios-ui-profiling-options branch April 23, 2026 11:17
antonis added a commit that referenced this pull request Apr 29, 2026
* refactor(ios): remove dead SentrySDKWrapper init surface

`SentrySDKWrapper.createOptionsWithDictionary:isSessionReplayEnabled:error:`
(and `setupWithDictionary:`, `startWithOptions:`, `enableAutoSessionTracking`,
`enableWatchdogTerminationTracking`) became dead when v8 moved init to
`RNSentryStart` (#5582 / #4442). The dead methods had a parallel test suite
that kept passing, which is exactly how the bugs in #6012 and #6014 shipped:
new code was added to `SentrySDKWrapper` because the tests there made it look
like the live surface.

Delete the dead methods, migrate the still-relevant test coverage to
`RNSentryStart`-based tests, drop duplicate tests already pinned on
`RNSentryStart`. `SentrySDKWrapper.h` is not in the podspec's
`public_header_files`, so no downstream consumers are affected. The kept
methods (`configureScope:`, `crash`, `close`, `crashedLastRun`, `debug`,
`releaseName`) continue to be used from `RNSentry.mm`.

Closes #6015.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(test): drop duplicate testStartBeforeBreadcrumbs… method

The migration in the previous commit added a `testStart…KeepsBreadcrumbWhenDevServerUrlIsNotPassed…`
method that already existed in the `RNSentryStart Tests` block. Compile failed
in CI with "Duplicate declaration of method". The earlier
`@interface RNSentryStart Tests` already covers this scenario; the migrated
copy is redundant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(test): use designated initializers for SentryException and SentryMessage

Warden flagged the migrated tests using `[SentryException alloc]` and
`[SentryMessage alloc]` without `init`. Both classes have `SENTRY_NO_INIT`
and require designated initializers (`initWithValue:type:` and
`initWithFormatted:`), so the alloc-only pattern is undefined behavior —
property assignments happened to work via zero-initialized memory but
the test could crash or behave inconsistently and mask real regressions.

The pattern was inherited from the original SentrySDKWrapper-based tests
this PR migrated; same fix applied across all 12 occurrences.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
antonis added a commit that referenced this pull request Apr 29, 2026
…filingOptions (#6060)

Add JUnit coverage for `RNSentryStart.configureAndroidProfiling` to catch a
future refactor moving it out of the live `getSentryAndroidOptions` call site
— the same regression shape as the iOS bug fixed in #6012, which slipped through
because the Android side had zero coverage for this wiring.

Closes #6016.

Co-authored-by: Claude Opus 4.7 (1M context) <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