From dfbadc48b2d83c2ce203d0ddfe731610e3b3151c Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 17 Apr 2026 11:08:04 +0200 Subject: [PATCH 1/3] fix(profiling): iOS UI profiling on v8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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) --- CHANGELOG.md | 4 + .../RNSentryCocoaTesterTests/RNSentryTests.m | 86 +++++++++++++++++++ packages/core/ios/RNSentryStart.m | 10 +++ 3 files changed, 100 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8beedc9e6..ff87aed889 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ - Warn Expo users at Metro startup when prebuilt native projects are missing Sentry configuration ([#5984](https://github.com/getsentry/sentry-react-native/pull/5984)) +### Fixes + +- fix(profiling): iOS UI profiling on v8 + ## 8.8.0 ### Features diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m index 76c41d3c66..aba14e4387 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m @@ -5,6 +5,7 @@ #import #import #import +#import #import #import @import Sentry; @@ -1372,6 +1373,91 @@ - (void)testStartBeforeBreadcrumbsCallbackDoesNotFiltersOutNonDevServerOrDsnRequ XCTAssertEqual(breadcrumb, result); } +#if SENTRY_TARGET_PROFILING_SUPPORTED +// Regression test for the v8.0.0 bug where the init path (RNSentryStart) did not +// handle `_experiments.profilingOptions`, silently dropping iOS UI profiling config. +// This pins the full entry point used by `initNativeSdk` in RNSentry.mm. +- (void)testStartWithDictionaryInstallsConfigureProfilingFromExperimentsProfilingOptions +{ + NSError *error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", + @"_experiments" : @ { + @"profilingOptions" : @ { + @"profileSessionSampleRate" : @1.0, + @"lifecycle" : @"trace", + @"startOnAppStart" : @YES, + }, + }, + }; + [RNSentryStart startWithOptions:mockedReactNativeDictionary error:&error]; + SentryOptions *actualOptions = PrivateSentrySDKOnly.options; + + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertNotNil(actualOptions.configureProfiling, + @"configureProfiling must be installed after startWithOptions when profilingOptions is " + @"present"); +} + +- (void)testStartCreateOptionsWithDictionaryProfilingOptionsInstallsConfigureProfiling +{ + NSError *error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", + @"_experiments" : @ { + @"profilingOptions" : @ { + @"profileSessionSampleRate" : @1.0, + @"lifecycle" : @"trace", + @"startOnAppStart" : @YES, + }, + }, + }; + SentryOptions *actualOptions = + [RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error]; + + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertNotNil(actualOptions.configureProfiling, + @"configureProfiling callback should be installed when profilingOptions is present"); +} + +- (void)testStartCreateOptionsWithDictionaryProfilingOptionsMissingDoesNotInstallConfigureProfiling +{ + NSError *error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", + }; + SentryOptions *actualOptions = + [RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error]; + + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertNil(actualOptions.configureProfiling, + @"configureProfiling callback should not be installed without profilingOptions"); +} + +- (void)testStartCreateOptionsWithDictionaryEmptyExperimentsDoesNotInstallConfigureProfiling +{ + NSError *error = nil; + + NSDictionary *_Nonnull mockedReactNativeDictionary = @{ + @"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456", + @"_experiments" : @ { }, + }; + SentryOptions *actualOptions = + [RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error]; + + XCTAssertNotNil(actualOptions, @"Did not create sentry options"); + XCTAssertNil(error, @"Should not pass no error"); + XCTAssertNil(actualOptions.configureProfiling, + @"configureProfiling callback should not be installed when profilingOptions is absent"); +} +#endif + - (void)testStartEventFromSentryCocoaReactNativeHasOriginAndEnvironmentTags { SentryEvent *testEvent = [[SentryEvent alloc] init]; diff --git a/packages/core/ios/RNSentryStart.m b/packages/core/ios/RNSentryStart.m index 03ca9e2ccd..c7faf91dae 100644 --- a/packages/core/ios/RNSentryStart.m +++ b/packages/core/ios/RNSentryStart.m @@ -100,6 +100,16 @@ + (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull) } } + // Configure iOS UI Profiling from _experiments.profilingOptions + NSDictionary *experiments = mutableOptions[@"_experiments"]; + if (experiments != nil && [experiments isKindOfClass:[NSDictionary class]]) { + NSDictionary *profilingOptions = experiments[@"profilingOptions"]; + if (profilingOptions != nil && [profilingOptions isKindOfClass:[NSDictionary class]]) { + [RNSentryExperimentalOptions configureProfilingWithOptions:profilingOptions + sentryOptions:sentryOptions]; + } + } + // Set strict trace continuation options if ([mutableOptions valueForKey:@"strictTraceContinuation"] != nil) { sentryOptions.strictTraceContinuation = From d1acea081edecfb156d3d4c20544be439cd7c40e Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 17 Apr 2026 11:17:10 +0200 Subject: [PATCH 2/3] review: house-style CHANGELOG and verify profiling fields land on probe - 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) --- CHANGELOG.md | 2 +- .../RNSentryCocoaTesterTests/RNSentryTests.m | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff87aed889..0b777b1552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ ### Fixes -- fix(profiling): iOS UI profiling on v8 +- Fix iOS UI profiling options being silently ignored since v8.0.0 ([#6012](https://github.com/getsentry/sentry-react-native/pull/6012)) ## 8.8.0 diff --git a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m index aba14e4387..b09a2073d0 100644 --- a/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m +++ b/packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m @@ -1399,6 +1399,12 @@ - (void)testStartWithDictionaryInstallsConfigureProfilingFromExperimentsProfilin XCTAssertNotNil(actualOptions.configureProfiling, @"configureProfiling must be installed after startWithOptions when profilingOptions is " @"present"); + + SentryProfileOptions *probe = [[SentryProfileOptions alloc] init]; + actualOptions.configureProfiling(probe); + XCTAssertEqual(probe.sessionSampleRate, 1.0f); + XCTAssertEqual(probe.lifecycle, SentryProfileLifecycleTrace); + XCTAssertTrue(probe.profileAppStarts); } - (void)testStartCreateOptionsWithDictionaryProfilingOptionsInstallsConfigureProfiling @@ -1422,6 +1428,12 @@ - (void)testStartCreateOptionsWithDictionaryProfilingOptionsInstallsConfigurePro XCTAssertNil(error, @"Should not pass no error"); XCTAssertNotNil(actualOptions.configureProfiling, @"configureProfiling callback should be installed when profilingOptions is present"); + + SentryProfileOptions *probe = [[SentryProfileOptions alloc] init]; + actualOptions.configureProfiling(probe); + XCTAssertEqual(probe.sessionSampleRate, 1.0f); + XCTAssertEqual(probe.lifecycle, SentryProfileLifecycleTrace); + XCTAssertTrue(probe.profileAppStarts); } - (void)testStartCreateOptionsWithDictionaryProfilingOptionsMissingDoesNotInstallConfigureProfiling From 80e477dadbb72dabce18e09cd47d5cafb51e8228 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 17 Apr 2026 11:32:25 +0200 Subject: [PATCH 3/3] changelog: simplify profiling fix wording --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b777b1552..2388eebb92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ ### Fixes -- Fix iOS UI profiling options being silently ignored since v8.0.0 ([#6012](https://github.com/getsentry/sentry-react-native/pull/6012)) +- Fix iOS UI profiling options being silently ignored ([#6012](https://github.com/getsentry/sentry-react-native/pull/6012)) ## 8.8.0