diff --git a/.github/last-release-runid b/.github/last-release-runid index 3635487bc13..de043f6b3fe 100644 --- a/.github/last-release-runid +++ b/.github/last-release-runid @@ -1 +1 @@ -12464271311 +12584459349 diff --git a/CHANGELOG.md b/CHANGELOG.md index cefaea27cbb..fdae78c01c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## 8.43.0 + +### Features + +- Session replay GA (#4662) +- Show session replay options as replay tags (#4639) + +### Fixes + +- Remove empty session replay tags (#4667) +- - `SentrySdkInfo.packages` should be an array (#4626) +- Use the same SdkInfo for envelope header and event (#4629) + +### Improvements + +- Improve compiler error message for missing Swift declarations due to APPLICATION_EXTENSION_API_ONLY (#4603) +- Mask screenshots for errors (#4623) +- Slightly speed up serializing scope (#4661) + +### Internal + +- Remove loading `integrations` names from `event.extra` (#4627) +- Add Hybrid SDKs API to add extra SDK packages (#4637) + ## 8.43.0-beta.1 ### Improvements diff --git a/Makefile b/Makefile index 1fc35000293..dc57858c12c 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ init: which brew || /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew bundle pre-commit install - clang-format --version | awk '{print $3}' > scripts/.clang-format-version + clang-format --version | awk '{print $$3}' > scripts/.clang-format-version swiftlint version > scripts/.swiftlint-version # installs the tools needed to test various CI tasks locally diff --git a/Package.swift b/Package.swift index 6add98a449b..c385a7d27e0 100644 --- a/Package.swift +++ b/Package.swift @@ -12,13 +12,13 @@ let package = Package( targets: [ .binaryTarget( name: "Sentry", - url: "https://github.com/getsentry/sentry-cocoa/releases/download/8.43.0-beta.1/Sentry.xcframework.zip", - checksum: "2c046fe71d1f55f3c68b09f44b298df7a5c741415ed2779a317f0a4c3b2f7af7" //Sentry-Static + url: "https://github.com/getsentry/sentry-cocoa/releases/download/8.43.0/Sentry.xcframework.zip", + checksum: "18b16b651630b865a91d6cf527ef79363156386e3e8568ae15d5d8718267d535" //Sentry-Static ), .binaryTarget( name: "Sentry-Dynamic", - url: "https://github.com/getsentry/sentry-cocoa/releases/download/8.43.0-beta.1/Sentry-Dynamic.xcframework.zip", - checksum: "ce3ffc5015b279c59c3f6012ce0bab5ffeb34e1db806408258cbfec7ea1d6e8b" //Sentry-Dynamic + url: "https://github.com/getsentry/sentry-cocoa/releases/download/8.43.0/Sentry-Dynamic.xcframework.zip", + checksum: "8da7680ad34c360503bc0d91ee4a8a690c44100066d913c601cd701a97e21c94" //Sentry-Dynamic ), .target ( name: "SentrySwiftUI", dependencies: ["Sentry", "SentryInternal"], diff --git a/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m b/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m index add7e76bfb4..3aa4ecc8023 100644 --- a/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m +++ b/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m @@ -28,11 +28,11 @@ - (BOOL)application:(UIApplication *)application [[SentryHttpStatusCodeRange alloc] initWithMin:400 max:599]; options.failedRequestStatusCodes = @[ httpStatusCodeRange ]; - options.experimental.sessionReplay.quality = SentryReplayQualityMedium; - options.experimental.sessionReplay.maskAllText = true; - options.experimental.sessionReplay.maskAllImages = true; - options.experimental.sessionReplay.sessionSampleRate = 0; - options.experimental.sessionReplay.onErrorSampleRate = 1; + options.sessionReplay.quality = SentryReplayQualityMedium; + options.sessionReplay.maskAllText = true; + options.sessionReplay.maskAllImages = true; + options.sessionReplay.sessionSampleRate = 0; + options.sessionReplay.onErrorSampleRate = 1; options.initialScope = ^(SentryScope *scope) { [scope setTagValue:@"" forKey:@""]; diff --git a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift index a793fa156d7..415f8f17f73 100644 --- a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift +++ b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift @@ -76,8 +76,8 @@ extension AppDelegate { options.debug = true if #available(iOS 16.0, *), enableSessionReplay { - options.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: 0, onErrorSampleRate: 1, maskAllText: true, maskAllImages: true) - options.experimental.sessionReplay.quality = .high + options.sessionReplay = SentryReplayOptions(sessionSampleRate: 0, onErrorSampleRate: 1, maskAllText: true, maskAllImages: true) + options.sessionReplay.quality = .high } if #available(iOS 15.0, *), enableMetricKit { diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift index ee92d4a10c3..87dbe088b67 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift @@ -10,7 +10,7 @@ struct SwiftUIApp: App { options.debug = true options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 - options.experimental.sessionReplay.sessionSampleRate = 1.0 + options.sessionReplay.sessionSampleRate = 1.0 options.initialScope = { scope in scope.injectGitInformation() return scope diff --git a/Sentry.podspec b/Sentry.podspec index 9a396643c2f..4e0db108047 100644 --- a/Sentry.podspec +++ b/Sentry.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Sentry" - s.version = "8.43.0-beta.1" + s.version = "8.43.0" s.summary = "Sentry client for cocoa" s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" diff --git a/SentryPrivate.podspec b/SentryPrivate.podspec index 68be3b98de7..1e559c33c60 100644 --- a/SentryPrivate.podspec +++ b/SentryPrivate.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SentryPrivate" - s.version = "8.43.0-beta.1" + s.version = "8.43.0" s.summary = "Sentry Private Library." s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" diff --git a/SentrySwiftUI.podspec b/SentrySwiftUI.podspec index 54b9bf70894..e4cbb24b234 100644 --- a/SentrySwiftUI.podspec +++ b/SentrySwiftUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SentrySwiftUI" - s.version = "8.43.0-beta.1" + s.version = "8.43.0" s.summary = "Sentry client for SwiftUI" s.homepage = "https://github.com/getsentry/sentry-cocoa" s.license = "mit" @@ -19,5 +19,5 @@ Pod::Spec.new do |s| s.watchos.framework = 'WatchKit' s.source_files = "Sources/SentrySwiftUI/**/*.{swift,h,m}" - s.dependency 'Sentry', "8.43.0-beta.1" + s.dependency 'Sentry', "8.43.0" end diff --git a/Sources/Sentry/Public/SentryOptions.h b/Sources/Sentry/Public/SentryOptions.h index 2f06444157f..d904f4bf471 100644 --- a/Sources/Sentry/Public/SentryOptions.h +++ b/Sources/Sentry/Public/SentryOptions.h @@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN @class SentryMeasurementValue; @class SentryReplayOptions; @class SentryScope; +@class SentryReplayOptions; NS_SWIFT_NAME(Options) @interface SentryOptions : NSObject @@ -370,6 +371,15 @@ NS_SWIFT_NAME(Options) #endif // SENTRY_UIKIT_AVAILABLE +#if SENTRY_TARGET_REPLAY_SUPPORTED + +/** + * Settings to configure the session replay. + */ +@property (nonatomic, strong) SentryReplayOptions *sessionReplay; + +#endif // SENTRY_TARGET_REPLAY_SUPPORTED + /** * When enabled, the SDK tracks performance for HTTP requests if auto performance tracking and * @c enableSwizzling are enabled. diff --git a/Sources/Sentry/SentryBaseIntegration.m b/Sources/Sentry/SentryBaseIntegration.m index 35ea3988672..c66018cb42d 100644 --- a/Sources/Sentry/SentryBaseIntegration.m +++ b/Sources/Sentry/SentryBaseIntegration.m @@ -148,21 +148,16 @@ - (BOOL)shouldBeEnabledWithOptions:(SentryOptions *)options [self logWithOptionName:@"attachViewHierarchy"]; return NO; } - +#endif +#if SENTRY_TARGET_REPLAY_SUPPORTED if (integrationOptions & kIntegrationOptionEnableReplay) { - if (@available(iOS 16.0, tvOS 16.0, *)) { - if (options.experimental.sessionReplay.onErrorSampleRate == 0 - && options.experimental.sessionReplay.sessionSampleRate == 0) { - [self logWithOptionName:@"sessionReplaySettings"]; - return NO; - } - } else { - [self logWithReason:@"Session replay requires iOS 16 or above"]; + if (options.sessionReplay.onErrorSampleRate == 0 + && options.sessionReplay.sessionSampleRate == 0) { + [self logWithOptionName:@"sessionReplaySettings"]; return NO; } } #endif - if ((integrationOptions & kIntegrationOptionEnableCrashHandler) && !options.enableCrashHandler) { [self logWithOptionName:@"enableCrashHandler"]; diff --git a/Sources/Sentry/SentryMeta.m b/Sources/Sentry/SentryMeta.m index e76a7f0261c..aa301a13d27 100644 --- a/Sources/Sentry/SentryMeta.m +++ b/Sources/Sentry/SentryMeta.m @@ -5,7 +5,7 @@ @implementation SentryMeta // Don't remove the static keyword. If you do the compiler adds the constant name to the global // symbol table and it might clash with other constants. When keeping the static keyword the // compiler replaces all occurrences with the value. -static NSString *versionString = @"8.43.0-beta.1"; +static NSString *versionString = @"8.43.0"; static NSString *sdkName = @"sentry.cocoa"; + (NSString *)versionString diff --git a/Sources/Sentry/SentryOptions.m b/Sources/Sentry/SentryOptions.m index 9d9fc80085c..fbb01307182 100644 --- a/Sources/Sentry/SentryOptions.m +++ b/Sources/Sentry/SentryOptions.m @@ -139,6 +139,11 @@ - (instancetype)init self.enableAppHangTrackingV2 = NO; self.enableReportNonFullyBlockingAppHangs = YES; #endif // SENTRY_HAS_UIKIT + +#if SENTRY_TARGET_REPLAY_SUPPORTED + self.sessionReplay = [[SentryReplayOptions alloc] init]; +#endif + self.enableAppHangTracking = YES; self.appHangTimeoutInterval = 2.0; self.enableAutoBreadcrumbTracking = YES; @@ -468,6 +473,13 @@ - (BOOL)validateOptions:(NSDictionary *)options #endif // SENTRY_HAS_UIKIT +#if SENTRY_TARGET_REPLAY_SUPPORTED + if ([options[@"sessionReplay"] isKindOfClass:NSDictionary.class]) { + self.sessionReplay = + [[SentryReplayOptions alloc] initWithDictionary:options[@"sessionReplay"]]; + } +#endif // SENTRY_TARGET_REPLAY_SUPPORTED + [self setBool:options[@"enableAppHangTracking"] block:^(BOOL value) { self->_enableAppHangTracking = value; }]; diff --git a/Sources/Sentry/SentrySessionReplayIntegration.m b/Sources/Sentry/SentrySessionReplayIntegration.m index 92a2b7785de..d045eca7120 100644 --- a/Sources/Sentry/SentrySessionReplayIntegration.m +++ b/Sources/Sentry/SentrySessionReplayIntegration.m @@ -66,9 +66,8 @@ - (instancetype)init - (instancetype)initForManualUse:(nonnull SentryOptions *)options { if (self = [super init]) { - [self setupWith:options.experimental.sessionReplay - enableTouchTracker:options.enableSwizzling]; - [self startWithOptions:options.experimental.sessionReplay fullSession:YES]; + [self setupWith:options.sessionReplay enableTouchTracker:options.enableSwizzling]; + [self startWithOptions:options.sessionReplay fullSession:YES]; } return self; } @@ -79,7 +78,7 @@ - (BOOL)installWithOptions:(nonnull SentryOptions *)options return NO; } - [self setupWith:options.experimental.sessionReplay enableTouchTracker:options.enableSwizzling]; + [self setupWith:options.sessionReplay enableTouchTracker:options.enableSwizzling]; return YES; } diff --git a/Sources/Swift/Integrations/SessionReplay/RRWeb/SentryRRWebOptionsEvent.swift b/Sources/Swift/Integrations/SessionReplay/RRWeb/SentryRRWebOptionsEvent.swift index 51ac516535b..2558819d3e7 100644 --- a/Sources/Swift/Integrations/SessionReplay/RRWeb/SentryRRWebOptionsEvent.swift +++ b/Sources/Swift/Integrations/SessionReplay/RRWeb/SentryRRWebOptionsEvent.swift @@ -4,16 +4,22 @@ import Foundation @objc class SentryRRWebOptionsEvent: SentryRRWebCustomEvent { init(timestamp: Date, options: SentryReplayOptions) { - super.init(timestamp: timestamp, tag: "options", payload: - [ - "sessionSampleRate": options.sessionSampleRate, - "errorSampleRate": options.onErrorSampleRate, - "maskAllText": options.maskAllText, - "maskAllImages": options.maskAllImages, - "quality": String(describing: options.quality), - "maskedViewClasses": options.maskedViewClasses.map(String.init(describing: )).joined(separator: ", "), - "unmaskedViewClasses": options.unmaskedViewClasses.map(String.init(describing: )).joined(separator: ", ") - ] - ) + var payload: [String: Any] = [ + "sessionSampleRate": options.sessionSampleRate, + "errorSampleRate": options.onErrorSampleRate, + "maskAllText": options.maskAllText, + "maskAllImages": options.maskAllImages, + "quality": String(describing: options.quality) + ] + + if !options.maskedViewClasses.isEmpty { + payload["maskedViewClasses"] = options.maskedViewClasses.map(String.init(describing:)).joined(separator: ", ") + } + + if !options.unmaskedViewClasses.isEmpty { + payload["unmaskedViewClasses"] = options.unmaskedViewClasses.map(String.init(describing:)).joined(separator: ", ") + } + + super.init(timestamp: timestamp, tag: "options", payload: payload) } } diff --git a/Sources/Swift/SentryExperimentalOptions.swift b/Sources/Swift/SentryExperimentalOptions.swift index b8fbb23f254..19c914bedf0 100644 --- a/Sources/Swift/SentryExperimentalOptions.swift +++ b/Sources/Swift/SentryExperimentalOptions.swift @@ -1,18 +1,5 @@ @objcMembers public class SentryExperimentalOptions: NSObject { - #if canImport(UIKit) - /** - * Settings to configure the session replay. - */ - public var sessionReplay = SentryReplayOptions(sessionSampleRate: 0, onErrorSampleRate: 0) - #endif - func validateOptions(_ options: [String: Any]?) { - #if canImport(UIKit) - if let sessionReplayOptions = options?["sessionReplay"] as? [String: Any] { - sessionReplay = SentryReplayOptions(dictionary: sessionReplayOptions) - } - #endif } - } diff --git a/Tests/HybridSDKTest/HybridPod.podspec b/Tests/HybridSDKTest/HybridPod.podspec index 6cb68960881..1b283db3387 100644 --- a/Tests/HybridSDKTest/HybridPod.podspec +++ b/Tests/HybridSDKTest/HybridPod.podspec @@ -13,6 +13,6 @@ Pod::Spec.new do |s| s.requires_arc = true s.frameworks = 'Foundation' s.swift_versions = "5.5" - s.dependency "Sentry/HybridSDK", "8.43.0-beta.1" + s.dependency "Sentry/HybridSDK", "8.43.0" s.source_files = "HybridTest.swift" end diff --git a/Tests/SentryTests/Integrations/SessionReplay/SentrySessionReplayIntegrationTests.swift b/Tests/SentryTests/Integrations/SessionReplay/SentrySessionReplayIntegrationTests.swift index cade64eb715..1d26d46cf3f 100644 --- a/Tests/SentryTests/Integrations/SessionReplay/SentrySessionReplayIntegrationTests.swift +++ b/Tests/SentryTests/Integrations/SessionReplay/SentrySessionReplayIntegrationTests.swift @@ -46,7 +46,7 @@ class SentrySessionReplayIntegrationTests: XCTestCase { private func startSDK(sessionSampleRate: Float, errorSampleRate: Float, enableSwizzling: Bool = true, noIntegrations: Bool = false, configure: ((Options) -> Void)? = nil) { SentrySDK.start { $0.dsn = "https://user@test.com/test" - $0.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: sessionSampleRate, onErrorSampleRate: errorSampleRate) + $0.sessionReplay = SentryReplayOptions(sessionSampleRate: sessionSampleRate, onErrorSampleRate: errorSampleRate) $0.setIntegrations(noIntegrations ? [] : [SentrySessionReplayIntegration.self]) $0.enableSwizzling = enableSwizzling $0.cacheDirectoryPath = FileManager.default.temporaryDirectory.path @@ -288,7 +288,7 @@ class SentrySessionReplayIntegrationTests: XCTestCase { } startSDK(sessionSampleRate: 1, errorSampleRate: 1) { options in - options.experimental.sessionReplay.maskedViewClasses = [AnotherLabel.self] + options.sessionReplay.maskedViewClasses = [AnotherLabel.self] } let sut = try getSut() @@ -301,7 +301,7 @@ class SentrySessionReplayIntegrationTests: XCTestCase { } startSDK(sessionSampleRate: 1, errorSampleRate: 1) { options in - options.experimental.sessionReplay.unmaskedViewClasses = [AnotherLabel.self] + options.sessionReplay.unmaskedViewClasses = [AnotherLabel.self] } let sut = try getSut() diff --git a/Tests/SentryTests/Integrations/SessionReplay/SentrySessionReplayTests.swift b/Tests/SentryTests/Integrations/SessionReplay/SentrySessionReplayTests.swift index 68377c17a0a..11d680a2a60 100644 --- a/Tests/SentryTests/Integrations/SessionReplay/SentrySessionReplayTests.swift +++ b/Tests/SentryTests/Integrations/SessionReplay/SentrySessionReplayTests.swift @@ -425,8 +425,8 @@ class SentrySessionReplayTests: XCTestCase { XCTAssertEqual(options["errorSampleRate"] as? Float, 1) XCTAssertEqual(options["maskAllText"] as? Bool, true) XCTAssertEqual(options["maskAllImages"] as? Bool, true) - XCTAssertEqual(options["maskedViewClasses"] as? String, "") - XCTAssertEqual(options["unmaskedViewClasses"] as? String, "") + XCTAssertNil(options["maskedViewClasses"]) + XCTAssertNil(options["unmaskedViewClasses"]) XCTAssertEqual(options["quality"] as? String, "medium") } diff --git a/Tests/SentryTests/SentryOptionsTest.m b/Tests/SentryTests/SentryOptionsTest.m index 5520ae55452..e99f71be7fd 100644 --- a/Tests/SentryTests/SentryOptionsTest.m +++ b/Tests/SentryTests/SentryOptionsTest.m @@ -623,7 +623,7 @@ - (void)testNSNull_SetsDefaultValue #if SENTRY_HAS_UIKIT @"enableUIViewControllerTracing" : [NSNull null], @"attachScreenshot" : [NSNull null], - @"sessionReplayOptions" : [NSNull null], + @"sessionReplay" : [NSNull null], #endif // SENTRY_HAS_UIKIT @"enableAppHangTracking" : [NSNull null], @"appHangTimeoutInterval" : [NSNull null], @@ -689,8 +689,8 @@ - (void)assertDefaultValues:(SentryOptions *)options XCTAssertEqual(options.enablePreWarmedAppStartTracing, NO); XCTAssertEqual(options.attachViewHierarchy, NO); XCTAssertEqual(options.reportAccessibilityIdentifier, YES); - XCTAssertEqual(options.experimental.sessionReplay.onErrorSampleRate, 0); - XCTAssertEqual(options.experimental.sessionReplay.sessionSampleRate, 0); + XCTAssertEqual(options.sessionReplay.onErrorSampleRate, 0); + XCTAssertEqual(options.sessionReplay.sessionSampleRate, 0); #endif // SENTRY_HAS_UIKIT #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" @@ -887,11 +887,10 @@ - (void)testSessionReplaySettingsInit { if (@available(iOS 16.0, tvOS 16.0, *)) { SentryOptions *options = [self getValidOptions:@{ - @"experimental" : - @ { @"sessionReplay" : @ { @"sessionSampleRate" : @2, @"errorSampleRate" : @4 } } + @"sessionReplay" : @ { @"sessionSampleRate" : @2, @"errorSampleRate" : @4 } }]; - XCTAssertEqual(options.experimental.sessionReplay.sessionSampleRate, 2); - XCTAssertEqual(options.experimental.sessionReplay.onErrorSampleRate, 4); + XCTAssertEqual(options.sessionReplay.sessionSampleRate, 2); + XCTAssertEqual(options.sessionReplay.onErrorSampleRate, 4); } } @@ -899,8 +898,8 @@ - (void)testSessionReplaySettingsDefaults { if (@available(iOS 16.0, tvOS 16.0, *)) { SentryOptions *options = [self getValidOptions:@{ @"sessionReplayOptions" : @ {} }]; - XCTAssertEqual(options.experimental.sessionReplay.sessionSampleRate, 0); - XCTAssertEqual(options.experimental.sessionReplay.onErrorSampleRate, 0); + XCTAssertEqual(options.sessionReplay.sessionSampleRate, 0); + XCTAssertEqual(options.sessionReplay.onErrorSampleRate, 0); } }