Skip to content

Commit dfbadc4

Browse files
antonisclaude
andcommitted
fix(profiling): iOS UI profiling on v8
`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>
1 parent 9278c8e commit dfbadc4

3 files changed

Lines changed: 100 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212

1313
- Warn Expo users at Metro startup when prebuilt native projects are missing Sentry configuration ([#5984](https://github.com/getsentry/sentry-react-native/pull/5984))
1414

15+
### Fixes
16+
17+
- fix(profiling): iOS UI profiling on v8
18+
1519
## 8.8.0
1620

1721
### Features

packages/core/RNSentryCocoaTester/RNSentryCocoaTesterTests/RNSentryTests.m

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#import <OCMock/OCMock.h>
66
#import <RNSentry/RNSentry.h>
77
#import <Sentry/PrivateSentrySDKOnly.h>
8+
#import <Sentry/SentryProfilingConditionals.h>
89
#import <UIKit/UIKit.h>
910
#import <XCTest/XCTest.h>
1011
@import Sentry;
@@ -1372,6 +1373,91 @@ - (void)testStartBeforeBreadcrumbsCallbackDoesNotFiltersOutNonDevServerOrDsnRequ
13721373
XCTAssertEqual(breadcrumb, result);
13731374
}
13741375

1376+
#if SENTRY_TARGET_PROFILING_SUPPORTED
1377+
// Regression test for the v8.0.0 bug where the init path (RNSentryStart) did not
1378+
// handle `_experiments.profilingOptions`, silently dropping iOS UI profiling config.
1379+
// This pins the full entry point used by `initNativeSdk` in RNSentry.mm.
1380+
- (void)testStartWithDictionaryInstallsConfigureProfilingFromExperimentsProfilingOptions
1381+
{
1382+
NSError *error = nil;
1383+
1384+
NSDictionary *_Nonnull mockedReactNativeDictionary = @{
1385+
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
1386+
@"_experiments" : @ {
1387+
@"profilingOptions" : @ {
1388+
@"profileSessionSampleRate" : @1.0,
1389+
@"lifecycle" : @"trace",
1390+
@"startOnAppStart" : @YES,
1391+
},
1392+
},
1393+
};
1394+
[RNSentryStart startWithOptions:mockedReactNativeDictionary error:&error];
1395+
SentryOptions *actualOptions = PrivateSentrySDKOnly.options;
1396+
1397+
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
1398+
XCTAssertNil(error, @"Should not pass no error");
1399+
XCTAssertNotNil(actualOptions.configureProfiling,
1400+
@"configureProfiling must be installed after startWithOptions when profilingOptions is "
1401+
@"present");
1402+
}
1403+
1404+
- (void)testStartCreateOptionsWithDictionaryProfilingOptionsInstallsConfigureProfiling
1405+
{
1406+
NSError *error = nil;
1407+
1408+
NSDictionary *_Nonnull mockedReactNativeDictionary = @{
1409+
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
1410+
@"_experiments" : @ {
1411+
@"profilingOptions" : @ {
1412+
@"profileSessionSampleRate" : @1.0,
1413+
@"lifecycle" : @"trace",
1414+
@"startOnAppStart" : @YES,
1415+
},
1416+
},
1417+
};
1418+
SentryOptions *actualOptions =
1419+
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];
1420+
1421+
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
1422+
XCTAssertNil(error, @"Should not pass no error");
1423+
XCTAssertNotNil(actualOptions.configureProfiling,
1424+
@"configureProfiling callback should be installed when profilingOptions is present");
1425+
}
1426+
1427+
- (void)testStartCreateOptionsWithDictionaryProfilingOptionsMissingDoesNotInstallConfigureProfiling
1428+
{
1429+
NSError *error = nil;
1430+
1431+
NSDictionary *_Nonnull mockedReactNativeDictionary = @{
1432+
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
1433+
};
1434+
SentryOptions *actualOptions =
1435+
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];
1436+
1437+
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
1438+
XCTAssertNil(error, @"Should not pass no error");
1439+
XCTAssertNil(actualOptions.configureProfiling,
1440+
@"configureProfiling callback should not be installed without profilingOptions");
1441+
}
1442+
1443+
- (void)testStartCreateOptionsWithDictionaryEmptyExperimentsDoesNotInstallConfigureProfiling
1444+
{
1445+
NSError *error = nil;
1446+
1447+
NSDictionary *_Nonnull mockedReactNativeDictionary = @{
1448+
@"dsn" : @"https://abcd@efgh.ingest.sentry.io/123456",
1449+
@"_experiments" : @ { },
1450+
};
1451+
SentryOptions *actualOptions =
1452+
[RNSentryStart createOptionsWithDictionary:mockedReactNativeDictionary error:&error];
1453+
1454+
XCTAssertNotNil(actualOptions, @"Did not create sentry options");
1455+
XCTAssertNil(error, @"Should not pass no error");
1456+
XCTAssertNil(actualOptions.configureProfiling,
1457+
@"configureProfiling callback should not be installed when profilingOptions is absent");
1458+
}
1459+
#endif
1460+
13751461
- (void)testStartEventFromSentryCocoaReactNativeHasOriginAndEnvironmentTags
13761462
{
13771463
SentryEvent *testEvent = [[SentryEvent alloc] init];

packages/core/ios/RNSentryStart.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ + (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
100100
}
101101
}
102102

103+
// Configure iOS UI Profiling from _experiments.profilingOptions
104+
NSDictionary *experiments = mutableOptions[@"_experiments"];
105+
if (experiments != nil && [experiments isKindOfClass:[NSDictionary class]]) {
106+
NSDictionary *profilingOptions = experiments[@"profilingOptions"];
107+
if (profilingOptions != nil && [profilingOptions isKindOfClass:[NSDictionary class]]) {
108+
[RNSentryExperimentalOptions configureProfilingWithOptions:profilingOptions
109+
sentryOptions:sentryOptions];
110+
}
111+
}
112+
103113
// Set strict trace continuation options
104114
if ([mutableOptions valueForKey:@"strictTraceContinuation"] != nil) {
105115
sentryOptions.strictTraceContinuation =

0 commit comments

Comments
 (0)