Skip to content

Conversation

@noahsmartin
Copy link
Contributor

@noahsmartin noahsmartin commented Nov 2, 2025

This change may look big, but I've done everything that can be done to make it as small as possible. It only looks big because the generated API json file changed, the actual lines of code added are ~800. So many files were touched because I had to remove a bunch of imports of the objc class

This converts SentryOptions.h/.m to SentryOptions.swift and in doing so removes the last blocker for using SPM. It also cleans up a lot of gross bridging we had to do, like with SentryExperimentalOptions and the beforeSendLog callback. This is now replaced with one piece of cross bridging, which I'll get to later...

Almost all the relevant implementation here is in Options.swift. This matches the old ObjC API almost exactly, except that the class is now final. The API json file changed a lot structurally just because it represents Swift and ObjC differently, not because the actual API changed.

The tricky part of this is that we can't call an objc header that uses a swift type from Swift code. (This works in xcodebuild but not cocoapods/SPM) To work around this the value that gets passed toSentrySDKInternal needs to be type-erased. This is the same as what we had to do with types defined in Swift on SentryOptions.h prior to this PR. To make it easy to follow this change I've kept the Swift type almost everywhere, but in a few header files I had to use SentryOptionsObjc which is just an alias for NSObject. There are extensions at the end of Options.swift that explains what is done in more detail.

#skip-changelog

Closes #6690

@codecov
Copy link

codecov bot commented Nov 2, 2025

❌ 9 Tests Failed:

Tests completed Failed Passed Skipped
3966 9 3957 27
View the top 3 failed test(s) by shortest run time
SentryLogSPMTests::testOptions_BeforeSendLog_ViaKVC_DirectProperty
Stack Traces | 0s run time
Restarting after unexpected exit, crash, or test timeout in SentryLogSPMTests.testOptions_BeforeSendLog_ViaKVC_DirectProperty(); summary will include totals from previous launches.
SentryTests.SentryLogSPMTests::testOptions_BeforeSendLog_CanBeCleared
Stack Traces | 0s run time
.../SentryTests/Helper/SentryLogSPMTests.swift:117 - [<SentryOptions 0x11f74c4f0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key beforeSendLogDynamic. (NSUnknownKeyException)
SentryTests.SentryLogSPMTests::testOptions_BeforeSendLog_CanDropLog
Stack Traces | 0s run time
.../SentryTests/Helper/SentryLogSPMTests.swift:62 - [<SentryOptions 0x120910120> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key beforeSendLogDynamic. (NSUnknownKeyException)
SentryTests.SentryLogSPMTests::testOptions_BeforeSendLog_CanFilterByLevel
Stack Traces | 0s run time
.../SentryTests/Helper/SentryLogSPMTests.swift:78 - [<SentryOptions 0x11ff15220> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key beforeSendLogDynamic. (NSUnknownKeyException)
SentryTests.SentryLogSPMTests::testOptions_BeforeSendLog_CanModifyAttributes
Stack Traces | 0s run time
.../SentryTests/Helper/SentryLogSPMTests.swift:99 - [<SentryOptions 0x11ff14ab0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key beforeSendLogDynamic. (NSUnknownKeyException)
SentryTests.SentryLogSPMTests::testOptions_BeforeSendLog_ViaKVC
Stack Traces | 0s run time
.../SentryTests/Helper/SentryLogSPMTests.swift:42 - [<SentryOptions 0x11ff14d20> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key beforeSendLogDynamic. (NSUnknownKeyException)
SentryTests.SentryLogSPMTests::testOptions_BeforeSendLog_ViaKVC_DirectProperty
Stack Traces | 0s run time
.../SentryTests/Helper/SentryLogSPMTests.swift:145 - XCTAssertNotNil failed - In non-SPM builds, 'beforeSendLog' property exists and should work via KVC
iOS_SwiftUI_UITests.LaunchUITests::testTTID_TTFD
Stack Traces | 0s run time
.../iOS-SwiftUI/iOS-SwiftUI-UITests/LaunchUITests.swift:48 - Failed to tap "Show TTD" Button: Timed out while evaluating UI query.
iOS_Swift_UITests.LaunchUITests::testNavigationTransaction
Stack Traces | 0s run time
.../iOS-Swift/iOS-Swift-UITests/LaunchUITests.swift:153 - XCTAssertTrue failed - Following spans not found: loadView, viewDidLoad, viewWillAppear, viewWillLayoutSubviews, layoutSubViews, viewDidLayoutSubviews, viewDidAppear
iOS_Swift_UITests.UserFeedbackUITests::testUIElementsWithDefaults
Stack Traces | 0s run time
.../iOS-Swift/iOS-Swift-UITests/UserFeedbackUITests.swift:43 - XCTAssertTrue failed

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@noahsmartin noahsmartin force-pushed the clientHubSwift branch 4 times, most recently from 5169bbd to 1b852ec Compare November 3, 2025 01:47
Base automatically changed from clientHubSwift to main November 3, 2025 14:24
@noahsmartin noahsmartin force-pushed the optionsInSwift branch 19 times, most recently from 031e40c to 3581fc0 Compare November 5, 2025 03:50
@github-actions
Copy link
Contributor

github-actions bot commented Nov 5, 2025

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1219.72 ms 1247.82 ms 28.10 ms
Size 23.75 KiB 1.01 MiB 1012.59 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
854ca12 1219.94 ms 1251.32 ms 31.38 ms
a6f5396 1211.81 ms 1245.63 ms 33.82 ms
d637379 1226.43 ms 1250.77 ms 24.34 ms
4a7a005 1229.15 ms 1243.35 ms 14.20 ms
d48a247 1243.59 ms 1273.27 ms 29.67 ms
079bcc8 1217.88 ms 1234.88 ms 17.00 ms
1fecbb8 1242.78 ms 1265.40 ms 22.62 ms
acac774 1217.76 ms 1253.29 ms 35.52 ms
2a6f451 1212.89 ms 1237.45 ms 24.56 ms
75b23cd 1227.10 ms 1253.61 ms 26.51 ms

App size

Revision Plain With Sentry Diff
854ca12 23.74 KiB 996.96 KiB 973.22 KiB
a6f5396 23.75 KiB 989.12 KiB 965.38 KiB
d637379 23.75 KiB 855.38 KiB 831.63 KiB
4a7a005 23.75 KiB 979.96 KiB 956.22 KiB
d48a247 23.75 KiB 994.73 KiB 970.99 KiB
079bcc8 23.74 KiB 874.07 KiB 850.33 KiB
1fecbb8 23.75 KiB 969.28 KiB 945.53 KiB
acac774 23.75 KiB 866.51 KiB 842.76 KiB
2a6f451 23.75 KiB 913.13 KiB 889.38 KiB
75b23cd 23.75 KiB 933.66 KiB 909.92 KiB

Previous results on branch: optionsInSwift

Startup times

Revision Plain With Sentry Diff
e2114d5 1216.94 ms 1237.02 ms 20.08 ms
b65d755 1234.36 ms 1258.38 ms 24.02 ms

App size

Revision Plain With Sentry Diff
e2114d5 23.75 KiB 1.03 MiB 1.01 MiB
b65d755 23.75 KiB 1.03 MiB 1.01 MiB

@noahsmartin noahsmartin force-pushed the optionsInSwift branch 4 times, most recently from 796b619 to 29baa9f Compare November 5, 2025 18:06
@noahsmartin noahsmartin force-pushed the optionsInSwift branch 6 times, most recently from e2fb674 to 42da6fb Compare November 6, 2025 10:12
@noahsmartin noahsmartin marked this pull request as ready for review November 6, 2025 10:14
@noahsmartin noahsmartin force-pushed the optionsInSwift branch 2 times, most recently from d078a0f to 26bfb46 Compare November 6, 2025 13:38
Copy link

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

Bug: Inconsistent beforeSendLog exposure Across Swift Packages

The beforeSendLog callback is still conditionally excluded from dictionary initialization when SWIFT_PACKAGE is defined (#if !SWIFT_PACKAGE), but the new Swift Options class (introduced in this PR) always exposes the beforeSendLog property without any conditional compilation. This creates an inconsistency where dictionary-based initialization (via SentryOptionsInternal.initWithDict) will not properly set the beforeSendLog callback when using Swift Package Manager, even though the property exists and is accessible on the Swift Options class. The #if !SWIFT_PACKAGE guard should be removed to match the Swift implementation.

Sources/Sentry/SentryOptionsInternal.m#L116-L120

#if !SWIFT_PACKAGE
if ([self isBlock:options[@"beforeSendLog"]]) {
sentryOptions.beforeSendLog = options[@"beforeSendLog"];
}
#endif // !SWIFT_PACKAGE

Fix in Cursor Fix in Web


@noahsmartin noahsmartin force-pushed the optionsInSwift branch 2 times, most recently from f2f0e74 to d0ea6bc Compare November 6, 2025 14:39
@noahsmartin noahsmartin force-pushed the optionsInSwift branch 3 times, most recently from e29a276 to 4238cf0 Compare November 6, 2025 16:52
@noahsmartin noahsmartin added the ready-to-merge Use this label to trigger all PR workflows label Nov 7, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2025

🚨 Detected changes in high risk code 🚨

High-risk code can easily blow up and is hard to test. We had severe bugs in the past. Be extra careful when changing these files, and have an extra careful look at these:

  • Sources/Sentry/SentryNetworkTracker.m
  • Sources/Sentry/SentryUIViewControllerSwizzling.m


NSNumber *callbackRate = _sentry_samplerCallbackRate(
options.tracesSampler, context, SENTRY_DEFAULT_TRACES_SAMPLE_RATE);
NSNumber *callbackRate = _sentry_samplerCallbackRate(options.tracesSampler, context, 0);
Copy link

Choose a reason for hiding this comment

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

Bug: Default Traces Sampling Changes Break Backward Compatibility

The _sentry_samplerCallbackRate function now receives a hardcoded default value of 0 instead of the previously used constant SENTRY_DEFAULT_TRACES_SAMPLE_RATE. This changes the SDK's behavior when a tracesSampler callback returns an invalid sample rate - it now defaults to 0 (no sampling) instead of the previous default. This breaks backward compatibility and causes all traces to be dropped when the callback returns invalid values, which wasn't the case before the refactor.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Use this label to trigger all PR workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ref: SentryOptions in Swift

3 participants