From 3fbeeb67f25803ed966b4b87c830c52fc6dacf39 Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Thu, 23 Oct 2025 16:08:09 -0300 Subject: [PATCH 1/9] chore: Restore PrivateSentrySDKOnlyTests --- Sentry.xcodeproj/project.pbxproj | 4 + .../PrivateSentrySDKOnlyTests.swift | 102 +++++++++--------- 2 files changed, 56 insertions(+), 50 deletions(-) diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 0166c545b26..745edaac8d9 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -978,6 +978,7 @@ D8FFE50C2703DBB400607131 /* SwizzlingCallTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8FFE50B2703DAAE00607131 /* SwizzlingCallTests.swift */; }; F40E42352EA1887000E53876 /* SentryFramesTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40E42342EA1887000E53876 /* SentryFramesTracker.swift */; }; F40E423C2EA18E8000E53876 /* SentryDelayedFramesTrackerWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40E423B2EA18E8000E53876 /* SentryDelayedFramesTrackerWrapper.swift */; }; + F40E42FB2EAAAD4500E53876 /* PrivateSentrySDKOnlyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40E42FA2EAAAD4500E53876 /* PrivateSentrySDKOnlyTests.swift */; }; F41362112E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362102E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift */; }; F41362132E1C566100B84443 /* SentryScopePersistentStore+User.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362122E1C566100B84443 /* SentryScopePersistentStore+User.swift */; }; F41362152E1C568400B84443 /* SentryScopePersistentStore+Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362142E1C568400B84443 /* SentryScopePersistentStore+Context.swift */; }; @@ -2357,6 +2358,7 @@ D8FFE50B2703DAAE00607131 /* SwizzlingCallTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwizzlingCallTests.swift; sourceTree = ""; }; F40E42342EA1887000E53876 /* SentryFramesTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryFramesTracker.swift; sourceTree = ""; }; F40E423B2EA18E8000E53876 /* SentryDelayedFramesTrackerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryDelayedFramesTrackerWrapper.swift; sourceTree = ""; }; + F40E42FA2EAAAD4500E53876 /* PrivateSentrySDKOnlyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateSentrySDKOnlyTests.swift; sourceTree = ""; }; F41362102E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Tags.swift"; sourceTree = ""; }; F41362122E1C566100B84443 /* SentryScopePersistentStore+User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+User.swift"; sourceTree = ""; }; F41362142E1C568400B84443 /* SentryScopePersistentStore+Context.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Context.swift"; sourceTree = ""; }; @@ -3060,6 +3062,7 @@ 92235CAF2E155B2600865983 /* SentryLoggerTests.swift */, 928BED2A2E16977A00B4D398 /* SentryLogBatcherTests.swift */, F49D41992DEA2FB000D9244E /* SentryCrashExceptionApplicationTests.swift */, + F40E42FA2EAAAD4500E53876 /* PrivateSentrySDKOnlyTests.swift */, 7B6438AD26A710E6000D0F65 /* Categories */, D8BC28D32C00C6A60054DA4D /* Extensions */, 7BD7299B24654CD500EA3610 /* Helper */, @@ -6395,6 +6398,7 @@ D8B76B062808066D000A58C4 /* SentryScreenshotIntegrationTests.swift in Sources */, 7B8CA85726DD4E6200DD872C /* SentryNetworkTrackerIntegrationTests.swift in Sources */, D452FE6D2DDC873A00AFF56F /* SentryWatchdogTerminationAttributesProcessorTests.swift in Sources */, + F40E42FB2EAAAD4500E53876 /* PrivateSentrySDKOnlyTests.swift in Sources */, 7BAF3DD2243DD05C008A5414 /* SentryTransportInitializerTests.swift in Sources */, 7B68D93625FF5F1A0082D139 /* SentryAppState+Equality.m in Sources */, 7B5CAF7E27F5AD3500ED0DB6 /* TestNSURLRequestBuilder.m in Sources */, diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index 3e8fa72b099..e49360773ff 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -1,5 +1,5 @@ -@testable import Sentry -import SentryTestUtils +@_spi(Private) @testable import Sentry +@_spi(Private) import SentryTestUtils import XCTest class PrivateSentrySDKOnlyTests: XCTestCase { @@ -16,7 +16,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { func testStoreEnvelope() { let client = TestClient(options: Options()) - SentrySDK.setCurrentHub(TestHub(client: client, andScope: nil)) + SentrySDKInternal.setCurrentHub(TestHub(client: client, andScope: nil)) let envelope = TestConstants.envelope PrivateSentrySDKOnly.store(envelope) @@ -28,7 +28,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { func testStoreEnvelopeWithUndhandled_MarksSessionAsCrashedAndDoesNotStartNewSession() throws { let client = TestClient(options: Options()) let hub = TestHub(client: client, andScope: nil) - SentrySDK.setCurrentHub(hub) + SentrySDKInternal.setCurrentHub(hub) hub.setTestSession() let sessionToBeCrashed = hub.session @@ -36,7 +36,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { PrivateSentrySDKOnly.store(envelope) let storedEnvelope = client?.storedEnvelopeInvocations.first - let attachedSessionData = storedEnvelope!.items.last!.data + let attachedSessionData = try XCTUnwrap(storedEnvelope!.items.last!.data) let attachedSession = try XCTUnwrap(try! JSONSerialization.jsonObject(with: attachedSessionData) as? [String: Any]) XCTAssertEqual(0, hub.startSessionInvocations) @@ -47,7 +47,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { func testCaptureEnvelope() { let client = TestClient(options: Options()) - SentrySDK.setCurrentHub(TestHub(client: client, andScope: nil)) + SentrySDKInternal.setCurrentHub(TestHub(client: client, andScope: nil)) let envelope = TestConstants.envelope PrivateSentrySDKOnly.capture(envelope) @@ -59,7 +59,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { func testCaptureEnvelopeWithUndhandled_MarksSessionAsCrashedAndStartsNewSession() throws { let client = TestClient(options: Options()) let hub = TestHub(client: client, andScope: nil) - SentrySDK.setCurrentHub(hub) + SentrySDKInternal.setCurrentHub(hub) hub.setTestSession() let sessionToBeCrashed = hub.session @@ -67,7 +67,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { PrivateSentrySDKOnly.capture(envelope) let capturedEnvelope = client?.captureEnvelopeInvocations.first - let attachedSessionData = capturedEnvelope!.items.last!.data + let attachedSessionData = try XCTUnwrap(capturedEnvelope!.items.last!.data) let attachedSession = try XCTUnwrap(try! JSONSerialization.jsonObject(with: attachedSessionData) as? [String: Any]) // Assert new session was started @@ -126,27 +126,19 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertNil(PrivateSentrySDKOnly.envelope(with: itemData)) } - func testGetDebugImages() { - let images = PrivateSentrySDKOnly.getDebugImages() - - // Only make sure we get some images. The actual tests are in - // SentryDebugImageProviderTests - XCTAssertGreaterThan(images.count, 100) - } - #if canImport(UIKit) func testGetAppStartMeasurement() { let appStartMeasurement = TestData.getAppStartMeasurement(type: .warm, runtimeInitSystemTimestamp: 1) - SentrySDK.setAppStartMeasurement(appStartMeasurement) + SentrySDKInternal.setAppStartMeasurement(appStartMeasurement) XCTAssertEqual(appStartMeasurement, PrivateSentrySDKOnly.appStartMeasurement) - SentrySDK.setAppStartMeasurement(nil) + SentrySDKInternal.setAppStartMeasurement(nil) XCTAssertNil(PrivateSentrySDKOnly.appStartMeasurement) } func testGetAppStartMeasurementWithSpansCold() throws { - SentrySDK.setAppStartMeasurement( + SentrySDKInternal.setAppStartMeasurement( TestData.getAppStartMeasurement(type: .cold, runtimeInitSystemTimestamp: 1) ) @@ -162,7 +154,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { } func testGetAppStartMeasurementWithSpansPreWarmed() throws { - SentrySDK.setAppStartMeasurement( + SentrySDKInternal.setAppStartMeasurement( TestData.getAppStartMeasurement( type: .warm, runtimeInitSystemTimestamp: 1, @@ -197,7 +189,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { } func testGetAppStartMeasurementWithSpansReturnsTimestampsInMs() throws { - SentrySDK.setAppStartMeasurement( + SentrySDKInternal.setAppStartMeasurement( TestData.getAppStartMeasurement( type: .cold, appStartTimestamp: Date(timeIntervalSince1970: 5), @@ -237,7 +229,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { let options = Options() options.dsn = TestConstants.dsnAsString(username: "SentryFramesTrackingIntegrationTests") let client = TestClient(options: options) - SentrySDK.setCurrentHub(TestHub(client: client, andScope: nil)) + SentrySDKInternal.setCurrentHub(TestHub(client: client, andScope: nil)) XCTAssertEqual(PrivateSentrySDKOnly.options, options) } @@ -254,7 +246,6 @@ class PrivateSentrySDKOnlyTests: XCTestCase { */ func testProfilingStartAndCollect() throws { let image = DebugMeta() - image.name = "sentrytest" image.imageAddress = "0x0000000105705000" image.imageVmAddress = "0x0000000105705000" image.codeFile = "codeFile" @@ -272,7 +263,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { let options = Options() options.dsn = TestConstants.dsnAsString(username: "SentryFramesTrackingIntegrationTests") let client = TestClient(options: options) - SentrySDK.setCurrentHub(TestHub(client: client, andScope: nil)) + SentrySDKInternal.setCurrentHub(TestHub(client: client, andScope: nil)) let traceIdA = SentryId() @@ -287,7 +278,6 @@ class PrivateSentrySDKOnlyTests: XCTestCase { let debugMeta = try XCTUnwrap(payload?["debug_meta"] as? [String: Any]) let images = try XCTUnwrap(debugMeta["images"] as? [[String: Any]]) let debugImage = try XCTUnwrap(images.first) - XCTAssertEqual(debugImage["name"] as? String, image.name) XCTAssertEqual(debugImage["image_addr"] as? String, image.imageAddress) XCTAssertEqual(debugImage["image_vmaddr"] as? String, image.imageVmAddress) XCTAssertEqual(debugImage["code_file"] as? String, image.codeFile) @@ -311,7 +301,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { let options = Options() options.dsn = TestConstants.dsnAsString(username: "SentryFramesTrackingIntegrationTests") let client = TestClient(options: options) - SentrySDK.setCurrentHub(TestHub(client: client, andScope: nil)) + SentrySDKInternal.setCurrentHub(TestHub(client: client, andScope: nil)) let traceIdA = SentryId() @@ -359,12 +349,13 @@ class PrivateSentrySDKOnlyTests: XCTestCase { #endif #if canImport(UIKit) + @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testCaptureReplayShouldCallReplayIntegration() { guard #available(iOS 16.0, tvOS 16.0, *) else { return } let options = Options() options.setIntegrations([TestSentrySessionReplayIntegration.self]) - SentrySDK.start(options: options) + SentrySDKInternal.start(options: options) PrivateSentrySDKOnly.captureReplay() @@ -379,32 +370,35 @@ class PrivateSentrySDKOnlyTests: XCTestCase { let client = TestClient(options: Options()) let scope = Scope() scope.replayId = VALID_REPLAY_ID - SentrySDK.setCurrentHub(TestHub(client: client, andScope: scope)) + SentrySDKInternal.setCurrentHub(TestHub(client: client, andScope: scope)) XCTAssertEqual(PrivateSentrySDKOnly.getReplayId(), VALID_REPLAY_ID) } + @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testAddReplayIgnoreClassesShouldNotFailWhenReplayIsAvailable() { let options = Options() options.setIntegrations([TestSentrySessionReplayIntegration.self]) - SentrySDK.start(options: options) + SentrySDKInternal.start(options: options) PrivateSentrySDKOnly.addReplayIgnoreClasses([UILabel.self]) } + @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testAddReplayRedactShouldNotFailWhenReplayIsAvailable() { let options = Options() options.setIntegrations([TestSentrySessionReplayIntegration.self]) - SentrySDK.start(options: options) + SentrySDKInternal.start(options: options) PrivateSentrySDKOnly.addReplayRedactClasses([UILabel.self]) } + @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testAddIgnoreContainer() throws { class IgnoreContainer: UIView {} - SentrySDK.start { - $0.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, onErrorSampleRate: 1) + SentrySDKInternal.start { + $0.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, onErrorSampleRate: 1) $0.setIntegrations([SentrySessionReplayIntegration.self]) } @@ -412,15 +406,16 @@ class PrivateSentrySDKOnlyTests: XCTestCase { let replayIntegration = try getFirstIntegrationAsReplay() - let redactBuilder = replayIntegration.viewPhotographer.getRedactBuild() + let redactBuilder = replayIntegration.viewPhotographer.getRedactBuilder() XCTAssertTrue(redactBuilder.isIgnoreContainerClassTestOnly(IgnoreContainer.self)) } + @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testAddRedactContainer() throws { class RedactContainer: UIView {} - SentrySDK.start { - $0.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, onErrorSampleRate: 1) + SentrySDKInternal.start { + $0.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, onErrorSampleRate: 1) $0.setIntegrations([SentrySessionReplayIntegration.self]) } @@ -428,25 +423,31 @@ class PrivateSentrySDKOnlyTests: XCTestCase { let replayIntegration = try getFirstIntegrationAsReplay() - let redactBuilder = replayIntegration.viewPhotographer.getRedactBuild() + let redactBuilder = replayIntegration.viewPhotographer.getRedactBuilder() XCTAssertTrue(redactBuilder.isRedactContainerClassTestOnly(RedactContainer.self)) } - func testAddExtraSdkPackages() { + func testAddExtraSdkPackages() throws { PrivateSentrySDKOnly.addSdkPackage("package1", version: "version1") PrivateSentrySDKOnly.addSdkPackage("package2", version: "version2") - XCTAssertEqual( - SentrySdkInfo.global().packages, - [ - ["name": "package1", "version": "version1"], - ["name": "package2", "version": "version2"] - ] - ) + // In swift the order is not guaranteed + let packages = try SentrySdkInfo.global().packages.sorted { package1, package2 in + try XCTUnwrap(package1["name"]) < XCTUnwrap(package2["name"]) + } + let expected = [ + ["name": "package1", "version": "version1"], + ["name": "package2", "version": "version2"] + ] + XCTAssertEqual(packages.count, expected.count) + for (index, package) in packages.enumerated() { + XCTAssertEqual(expected[index]["name"], package["name"]) + XCTAssertEqual(expected[index]["version"], package["version"]) + } } private func getFirstIntegrationAsReplay() throws -> SentrySessionReplayIntegration { - return try XCTUnwrap(SentrySDK.currentHub().installedIntegrations().first as? SentrySessionReplayIntegration) + return try XCTUnwrap(SentrySDKInternal.currentHub().installedIntegrations().first as? SentrySessionReplayIntegration) } private let VALID_REPLAY_ID = "0eac7ab503354dd5819b03e263627a29" @@ -481,17 +482,18 @@ class PrivateSentrySDKOnlyTests: XCTestCase { func testSetTrace() { // -- Arrange -- let traceId = SentryId() - let spanId = SentrySpanId() + let spanId = SpanId() let scope = Scope() - let hub = TestHub(client: nil, andScope: scope) - SentrySDK.setCurrentHub(hub) + let client = TestClient(options: Options()) + let hub = TestHub(client: client, andScope: scope) + SentrySDKInternal.setCurrentHub(hub) // -- Act -- PrivateSentrySDKOnly.setTrace(traceId, spanId: spanId) // -- Assert -- - XCTAssertEqual(scope.propagationContext?.traceId, traceId) - XCTAssertEqual(scope.propagationContext?.spanId, spanId) + XCTAssertEqual(scope.propagationContext.traceId, traceId) + XCTAssertEqual(scope.propagationContext.spanId, spanId) } } From 381272f599647769e5e9977d02693a4dfc8d4325 Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Thu, 23 Oct 2025 16:22:06 -0300 Subject: [PATCH 2/9] feat: Add view hierarchy attachment support for downstream SDKs --- CHANGELOG.md | 1 + Sources/Sentry/PrivateSentrySDKOnly.m | 13 ++++++++ .../HybridPublic/PrivateSentrySDKOnly.h | 13 ++++++++ .../PrivateSentrySDKOnlyTests.swift | 30 +++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index deeddf23807..9e3c134c263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ ### Improvements - Replace deprecated SCNetworkReachability with NWPathMonitor (#6019) +- Enable ViewHierarchy attachments for downstream SDKs (like sentry-godot) (#6521) ## 8.57.0 diff --git a/Sources/Sentry/PrivateSentrySDKOnly.m b/Sources/Sentry/PrivateSentrySDKOnly.m index 20f85e5e15c..e2cb12f4e12 100644 --- a/Sources/Sentry/PrivateSentrySDKOnly.m +++ b/Sources/Sentry/PrivateSentrySDKOnly.m @@ -1,5 +1,6 @@ #import "PrivateSentrySDKOnly.h" #import "SentryAppStartMeasurement.h" +#import "SentryAttachment+Private.h" #import "SentryBreadcrumb+Private.h" #import "SentryClient.h" #import "SentryHub+Private.h" @@ -302,6 +303,18 @@ + (SentryBreadcrumb *)breadcrumbWithDictionary:(NSDictionary *)dictionary return [[SentryBreadcrumb alloc] initWithDictionary:dictionary]; } ++ (void)addViewHierarchyAttachment:(NSString *)path +{ + SentryAttachment *attachment = + [[SentryAttachment alloc] initWithPath:path + filename:@"view-hierarchy.json" + contentType:@"application/json" + attachmentType:kSentryAttachmentTypeViewHierarchy]; + + [SentrySDKInternal.currentHub + configureScope:^(SentryScope *scope) { [scope addAttachment:attachment]; }]; +} + #if SENTRY_TARGET_REPLAY_SUPPORTED + (UIView *)sessionReplayMaskingOverlay:(id)options diff --git a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h index d82a162794c..d26ac24c006 100644 --- a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h +++ b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h @@ -198,6 +198,19 @@ typedef void (^SentryOnAppStartMeasurementAvailable)( + (SentryBreadcrumb *)breadcrumbWithDictionary:(NSDictionary *)dictionary; +/** + * Adds an view attachment to the Scope's list of attachments. + * File is not read until an event needs to be sent. + * The view hierarchy json should follow the representation defined in RFC#33 + * https://github.com/getsentry/rfcs/blob/main/text/0033-view-hierarchy.md + * + * This method is intended for downstream SDKs like sentry-godot which havbe a custom view hierarchy + * logic. + * + * @param path The path to the view-hierarchy.json file + */ ++ (void)addViewHierarchyAttachment:(NSString *)path; + @end NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index e49360773ff..76fbb46565b 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -496,4 +496,34 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertEqual(scope.propagationContext.traceId, traceId) XCTAssertEqual(scope.propagationContext.spanId, spanId) } + + func testAddViewHierarchyAttachment() throws { + // -- Arrange -- + let scope = Scope() + let client = TestClient(options: Options()) + let hub = TestHub(client: client, andScope: scope) + SentrySDKInternal.setCurrentHub(hub) + + let tempDir = NSTemporaryDirectory() + let filePath = (tempDir as NSString).appendingPathComponent("test-view-hierarchy.json") + let testData = Data("{\"test\": \"data\"}".utf8) + try testData.write(to: URL(fileURLWithPath: filePath)) + + // -- Act -- + PrivateSentrySDKOnly.addViewHierarchyAttachment(filePath) + + // -- Assert -- + let attachments = scope.attachments + XCTAssertEqual(attachments.count, 1) + + let attachment = try XCTUnwrap(attachments.first) + XCTAssertEqual(attachment.filename, "view-hierarchy.json") + XCTAssertEqual(attachment.contentType, "application/json") + XCTAssertEqual(attachment.attachmentType, SentryAttachmentType.viewHierarchy) + XCTAssertEqual(attachment.path, filePath) + + // Clean up + try? FileManager.default.removeItem(atPath: filePath) + } + } From f3789eca0faf0251fa2bd29189026ef7aee6399d Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Wed, 29 Oct 2025 15:48:38 -0300 Subject: [PATCH 3/9] Remove `addViewHierarchyAttachment` from `PrivateSentrySDKOnly` --- Sources/Sentry/PrivateSentrySDKOnly.m | 13 ------------- .../include/HybridPublic/PrivateSentrySDKOnly.h | 13 ------------- scripts/.swiftlint-version | 2 +- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/Sources/Sentry/PrivateSentrySDKOnly.m b/Sources/Sentry/PrivateSentrySDKOnly.m index e2cb12f4e12..20f85e5e15c 100644 --- a/Sources/Sentry/PrivateSentrySDKOnly.m +++ b/Sources/Sentry/PrivateSentrySDKOnly.m @@ -1,6 +1,5 @@ #import "PrivateSentrySDKOnly.h" #import "SentryAppStartMeasurement.h" -#import "SentryAttachment+Private.h" #import "SentryBreadcrumb+Private.h" #import "SentryClient.h" #import "SentryHub+Private.h" @@ -303,18 +302,6 @@ + (SentryBreadcrumb *)breadcrumbWithDictionary:(NSDictionary *)dictionary return [[SentryBreadcrumb alloc] initWithDictionary:dictionary]; } -+ (void)addViewHierarchyAttachment:(NSString *)path -{ - SentryAttachment *attachment = - [[SentryAttachment alloc] initWithPath:path - filename:@"view-hierarchy.json" - contentType:@"application/json" - attachmentType:kSentryAttachmentTypeViewHierarchy]; - - [SentrySDKInternal.currentHub - configureScope:^(SentryScope *scope) { [scope addAttachment:attachment]; }]; -} - #if SENTRY_TARGET_REPLAY_SUPPORTED + (UIView *)sessionReplayMaskingOverlay:(id)options diff --git a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h index d26ac24c006..d82a162794c 100644 --- a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h +++ b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h @@ -198,19 +198,6 @@ typedef void (^SentryOnAppStartMeasurementAvailable)( + (SentryBreadcrumb *)breadcrumbWithDictionary:(NSDictionary *)dictionary; -/** - * Adds an view attachment to the Scope's list of attachments. - * File is not read until an event needs to be sent. - * The view hierarchy json should follow the representation defined in RFC#33 - * https://github.com/getsentry/rfcs/blob/main/text/0033-view-hierarchy.md - * - * This method is intended for downstream SDKs like sentry-godot which havbe a custom view hierarchy - * logic. - * - * @param path The path to the view-hierarchy.json file - */ -+ (void)addViewHierarchyAttachment:(NSString *)path; - @end NS_ASSUME_NONNULL_END diff --git a/scripts/.swiftlint-version b/scripts/.swiftlint-version index 0b09455034e..92c648bd8c3 100644 --- a/scripts/.swiftlint-version +++ b/scripts/.swiftlint-version @@ -1 +1 @@ -0.61.0 +0.62.2 From 05d5a541822e6b56bef05d842c8b9cd5391514fb Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Wed, 29 Oct 2025 15:48:57 -0300 Subject: [PATCH 4/9] Update test --- .../PrivateSentrySDKOnlyTests.swift | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index 76fbb46565b..e49360773ff 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -496,34 +496,4 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertEqual(scope.propagationContext.traceId, traceId) XCTAssertEqual(scope.propagationContext.spanId, spanId) } - - func testAddViewHierarchyAttachment() throws { - // -- Arrange -- - let scope = Scope() - let client = TestClient(options: Options()) - let hub = TestHub(client: client, andScope: scope) - SentrySDKInternal.setCurrentHub(hub) - - let tempDir = NSTemporaryDirectory() - let filePath = (tempDir as NSString).appendingPathComponent("test-view-hierarchy.json") - let testData = Data("{\"test\": \"data\"}".utf8) - try testData.write(to: URL(fileURLWithPath: filePath)) - - // -- Act -- - PrivateSentrySDKOnly.addViewHierarchyAttachment(filePath) - - // -- Assert -- - let attachments = scope.attachments - XCTAssertEqual(attachments.count, 1) - - let attachment = try XCTUnwrap(attachments.first) - XCTAssertEqual(attachment.filename, "view-hierarchy.json") - XCTAssertEqual(attachment.contentType, "application/json") - XCTAssertEqual(attachment.attachmentType, SentryAttachmentType.viewHierarchy) - XCTAssertEqual(attachment.path, filePath) - - // Clean up - try? FileManager.default.removeItem(atPath: filePath) - } - } From 4cda8a8536ce666f067c91c23b9574538b795232 Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Wed, 29 Oct 2025 15:49:31 -0300 Subject: [PATCH 5/9] Expose attachment type on `SentryAttachment` --- Sources/Sentry/Public/SentryAttachment.h | 37 ++++++++++++++++ .../Sentry/include/SentryAttachment+Private.h | 42 ------------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/Sources/Sentry/Public/SentryAttachment.h b/Sources/Sentry/Public/SentryAttachment.h index 8b0e904fe46..367c3750099 100644 --- a/Sources/Sentry/Public/SentryAttachment.h +++ b/Sources/Sentry/Public/SentryAttachment.h @@ -8,6 +8,14 @@ NS_ASSUME_NONNULL_BEGIN +/** + * Attachment Type + */ +typedef NS_ENUM(NSInteger, SentryAttachmentType) { + kSentryAttachmentTypeEventAttachment, + kSentryAttachmentTypeViewHierarchy +}; + /** * You can use an attachment to store additional files alongside an event. */ @@ -62,6 +70,30 @@ SENTRY_NO_INIT filename:(NSString *)filename contentType:(nullable NSString *)contentType; +/** + * Initializes an attachment with data. + * @param data The data for the attachment. + * @param filename The name of the attachment to display in Sentry. + * @param contentType The content type of the attachment. Default is @c "application/octet-stream". + * @param attachmentType The type of the attachment. Default is @c "EventAttachment". + */ +- (instancetype)initWithData:(NSData *)data + filename:(NSString *)filename + contentType:(nullable NSString *)contentType + attachmentType:(SentryAttachmentType)attachmentType; + +/** + * Initializes an attachment with data. + * @param path The path of the file whose contents you want to upload to Sentry. + * @param filename The name of the attachment to display in Sentry. + * @param contentType The content type of the attachment. Default is @c "application/octet-stream". + * @param attachmentType The type of the attachment. Default is@c "EventAttachment". + */ +- (instancetype)initWithPath:(NSString *)path + filename:(NSString *)filename + contentType:(nullable NSString *)contentType + attachmentType:(SentryAttachmentType)attachmentType; + /** * The data of the attachment. */ @@ -82,6 +114,11 @@ SENTRY_NO_INIT */ @property (readonly, nonatomic, copy, nullable) NSString *contentType; +/** + * The type of the attachment. + */ +@property (readonly, nonatomic) SentryAttachmentType attachmentType; + @end NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentryAttachment+Private.h b/Sources/Sentry/include/SentryAttachment+Private.h index 32c012e9324..f44435f3e80 100644 --- a/Sources/Sentry/include/SentryAttachment+Private.h +++ b/Sources/Sentry/include/SentryAttachment+Private.h @@ -6,50 +6,8 @@ NS_ASSUME_NONNULL_BEGIN FOUNDATION_EXPORT NSString *const kSentryAttachmentTypeNameEventAttachment; FOUNDATION_EXPORT NSString *const kSentryAttachmentTypeNameViewHierarchy; -/** - * Attachment Type - */ -typedef NS_ENUM(NSInteger, SentryAttachmentType) { - kSentryAttachmentTypeEventAttachment, - kSentryAttachmentTypeViewHierarchy -}; - NSString *nameForSentryAttachmentType(SentryAttachmentType attachmentType); SentryAttachmentType typeForSentryAttachmentName(NSString *_Nullable name); -@interface SentryAttachment () -SENTRY_NO_INIT - -/** - * Initializes an attachment with data. - * @param data The data for the attachment. - * @param filename The name of the attachment to display in Sentry. - * @param contentType The content type of the attachment. Default is @c "application/octet-stream". - * @param attachmentType The type of the attachment. Default is @c "EventAttachment". - */ -- (instancetype)initWithData:(NSData *)data - filename:(NSString *)filename - contentType:(nullable NSString *)contentType - attachmentType:(SentryAttachmentType)attachmentType; - -/** - * Initializes an attachment with data. - * @param path The path of the file whose contents you want to upload to Sentry. - * @param filename The name of the attachment to display in Sentry. - * @param contentType The content type of the attachment. Default is @c "application/octet-stream". - * @param attachmentType The type of the attachment. Default is@c "EventAttachment". - */ -- (instancetype)initWithPath:(NSString *)path - filename:(NSString *)filename - contentType:(nullable NSString *)contentType - attachmentType:(SentryAttachmentType)attachmentType; - -/** - * The type of the attachment. - */ -@property (readonly, nonatomic) SentryAttachmentType attachmentType; - -@end - NS_ASSUME_NONNULL_END From 31eb2d38092b287f1f52993fdbca1eb9fb99c5ca Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Wed, 29 Oct 2025 15:50:46 -0300 Subject: [PATCH 6/9] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e3c134c263..8b136a92bd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ ### Improvements - Replace deprecated SCNetworkReachability with NWPathMonitor (#6019) -- Enable ViewHierarchy attachments for downstream SDKs (like sentry-godot) (#6521) +- Expose attachment type on `SentryAttachment` for downstream SDKs (like sentry-godot) (#6521) ## 8.57.0 From ed5f84bfa18c944a0a431848e1bc12c733e9b8b8 Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Wed, 29 Oct 2025 16:10:16 -0300 Subject: [PATCH 7/9] Undo merge conflict --- Tests/SentryTests/PrivateSentrySDKOnlyTests.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index 874687376a6..c9e2de9991a 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -392,7 +392,6 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertEqual(PrivateSentrySDKOnly.getReplayId(), VALID_REPLAY_ID) } - @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testAddReplayIgnoreClassesShouldNotFailWhenReplayIsAvailable() { let options = Options.noIntegrations() options.sessionReplay = .init() @@ -401,7 +400,6 @@ class PrivateSentrySDKOnlyTests: XCTestCase { PrivateSentrySDKOnly.addReplayIgnoreClasses([UILabel.self]) } - @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testAddReplayRedactShouldNotFailWhenReplayIsAvailable() { let options = Options() options.sessionReplay = .init() @@ -410,7 +408,6 @@ class PrivateSentrySDKOnlyTests: XCTestCase { PrivateSentrySDKOnly.addReplayRedactClasses([UILabel.self]) } - @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testAddIgnoreContainer() throws { class IgnoreContainer: UIView {} @@ -427,7 +424,6 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertTrue(redactBuilder.isIgnoreContainerClassTestOnly(IgnoreContainer.self)) } - @available(*, deprecated, message: "This is deprecated because SentryOptions integrations is deprecated") func testAddRedactContainer() throws { class RedactContainer: UIView {} From 5300caef016ffdc2b0e089e68e3f652b484cf02a Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Thu, 30 Oct 2025 12:05:40 -0300 Subject: [PATCH 8/9] Run generate api --- sdk_api.json | 409 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) diff --git a/sdk_api.json b/sdk_api.json index b6908bb4328..8e623dc50b8 100644 --- a/sdk_api.json +++ b/sdk_api.json @@ -621,6 +621,114 @@ ], "init_kind": "Designated" }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(data:filename:contentType:attachmentType:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Attachment", + "printedName": "Sentry.Attachment", + "usr": "c:objc(cs)SentryAttachment" + }, + { + "kind": "TypeNominal", + "name": "Data", + "printedName": "Foundation.Data", + "usr": "s:10Foundation4DataV" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + } + ], + "declKind": "Constructor", + "usr": "c:objc(cs)SentryAttachment(im)initWithData:filename:contentType:attachmentType:", + "moduleName": "Sentry", + "objc_name": "initWithData:filename:contentType:attachmentType:", + "declAttributes": [ + "ObjC", + "Dynamic" + ], + "init_kind": "Designated" + }, + { + "kind": "Constructor", + "name": "init", + "printedName": "init(path:filename:contentType:attachmentType:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Attachment", + "printedName": "Sentry.Attachment", + "usr": "c:objc(cs)SentryAttachment" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + }, + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Swift.String?", + "children": [ + { + "kind": "TypeNominal", + "name": "String", + "printedName": "Swift.String", + "usr": "s:SS" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + } + ], + "declKind": "Constructor", + "usr": "c:objc(cs)SentryAttachment(im)initWithPath:filename:contentType:attachmentType:", + "moduleName": "Sentry", + "objc_name": "initWithPath:filename:contentType:attachmentType:", + "declAttributes": [ + "ObjC", + "Dynamic" + ], + "init_kind": "Designated" + }, { "kind": "Var", "name": "data", @@ -860,6 +968,54 @@ "accessorKind": "get" } ] + }, + { + "kind": "Var", + "name": "attachmentType", + "printedName": "attachmentType", + "children": [ + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + } + ], + "declKind": "Var", + "usr": "c:objc(cs)SentryAttachment(py)attachmentType", + "moduleName": "Sentry", + "isOpen": true, + "objc_name": "attachmentType", + "declAttributes": [ + "ObjC", + "Dynamic" + ], + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + } + ], + "declKind": "Accessor", + "usr": "c:objc(cs)SentryAttachment(im)attachmentType", + "moduleName": "Sentry", + "isOpen": true, + "objc_name": "attachmentType", + "declAttributes": [ + "DiscardableResult", + "ObjC", + "Dynamic" + ], + "accessorKind": "get" + } + ] } ], "declKind": "Class", @@ -23767,6 +23923,259 @@ } ] }, + { + "kind": "TypeDecl", + "name": "SentryAttachmentType", + "printedName": "SentryAttachmentType", + "children": [ + { + "kind": "Constructor", + "name": "init", + "printedName": "init(rawValue:)", + "children": [ + { + "kind": "TypeNominal", + "name": "Optional", + "printedName": "Sentry.SentryAttachmentType?", + "children": [ + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + } + ], + "usr": "s:Sq" + }, + { + "kind": "TypeNominal", + "name": "Int", + "printedName": "Swift.Int", + "usr": "s:Si" + } + ], + "declKind": "Constructor", + "usr": "s:So20SentryAttachmentTypeV8rawValueABSgSi_tcfc", + "mangledName": "$sSo20SentryAttachmentTypeV8rawValueABSgSi_tcfc", + "moduleName": "Sentry", + "implicit": true, + "init_kind": "Designated" + }, + { + "kind": "Var", + "name": "rawValue", + "printedName": "rawValue", + "children": [ + { + "kind": "TypeNominal", + "name": "Int", + "printedName": "Swift.Int", + "usr": "s:Si" + } + ], + "declKind": "Var", + "usr": "s:So20SentryAttachmentTypeV8rawValueSivp", + "mangledName": "$sSo20SentryAttachmentTypeV8rawValueSivp", + "moduleName": "Sentry", + "implicit": true, + "accessors": [ + { + "kind": "Accessor", + "name": "Get", + "printedName": "Get()", + "children": [ + { + "kind": "TypeNominal", + "name": "Int", + "printedName": "Swift.Int", + "usr": "s:Si" + } + ], + "declKind": "Accessor", + "usr": "s:So20SentryAttachmentTypeV8rawValueSivg", + "mangledName": "$sSo20SentryAttachmentTypeV8rawValueSivg", + "moduleName": "Sentry", + "implicit": true, + "accessorKind": "get" + } + ] + }, + { + "kind": "TypeAlias", + "name": "RawValue", + "printedName": "RawValue", + "children": [ + { + "kind": "TypeNominal", + "name": "Int", + "printedName": "Swift.Int", + "usr": "s:Si" + } + ], + "declKind": "TypeAlias", + "usr": "s:So20SentryAttachmentTypeV8RawValuea", + "mangledName": "$sSo20SentryAttachmentTypeV8RawValuea", + "moduleName": "Sentry", + "implicit": true + }, + { + "kind": "Var", + "name": "eventAttachment", + "printedName": "eventAttachment", + "children": [ + { + "kind": "TypeFunc", + "name": "Function", + "printedName": "(Sentry.SentryAttachmentType.Type) -> Sentry.SentryAttachmentType", + "children": [ + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + }, + { + "kind": "TypeNominal", + "name": "Metatype", + "printedName": "Sentry.SentryAttachmentType.Type", + "children": [ + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + } + ] + } + ] + } + ], + "declKind": "EnumElement", + "usr": "c:@E@SentryAttachmentType@kSentryAttachmentTypeEventAttachment", + "moduleName": "Sentry", + "declAttributes": [ + "ObjC" + ] + }, + { + "kind": "Var", + "name": "viewHierarchy", + "printedName": "viewHierarchy", + "children": [ + { + "kind": "TypeFunc", + "name": "Function", + "printedName": "(Sentry.SentryAttachmentType.Type) -> Sentry.SentryAttachmentType", + "children": [ + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + }, + { + "kind": "TypeNominal", + "name": "Metatype", + "printedName": "Sentry.SentryAttachmentType.Type", + "children": [ + { + "kind": "TypeNominal", + "name": "SentryAttachmentType", + "printedName": "Sentry.SentryAttachmentType", + "usr": "c:@E@SentryAttachmentType" + } + ] + } + ] + } + ], + "declKind": "EnumElement", + "usr": "c:@E@SentryAttachmentType@kSentryAttachmentTypeViewHierarchy", + "moduleName": "Sentry", + "declAttributes": [ + "ObjC" + ] + } + ], + "declKind": "Enum", + "usr": "c:@E@SentryAttachmentType", + "moduleName": "Sentry", + "objc_name": "SentryAttachmentType", + "declAttributes": [ + "SynthesizedProtocol", + "ObjC", + "SynthesizedProtocol", + "Sendable", + "Dynamic" + ], + "enumRawTypeName": "Int", + "conformances": [ + { + "kind": "Conformance", + "name": "Copyable", + "printedName": "Copyable", + "usr": "s:s8CopyableP", + "mangledName": "$ss8CopyableP" + }, + { + "kind": "Conformance", + "name": "Escapable", + "printedName": "Escapable", + "usr": "s:s9EscapableP", + "mangledName": "$ss9EscapableP" + }, + { + "kind": "Conformance", + "name": "RawRepresentable", + "printedName": "RawRepresentable", + "children": [ + { + "kind": "TypeWitness", + "name": "RawValue", + "printedName": "RawValue", + "children": [ + { + "kind": "TypeNameAlias", + "name": "RawValue", + "printedName": "Sentry.SentryAttachmentType.RawValue", + "children": [ + { + "kind": "TypeNominal", + "name": "Int", + "printedName": "Swift.Int", + "usr": "s:Si" + } + ] + } + ] + } + ], + "usr": "s:SY", + "mangledName": "$sSY" + }, + { + "kind": "Conformance", + "name": "Sendable", + "printedName": "Sendable", + "usr": "s:s8SendableP", + "mangledName": "$ss8SendableP" + }, + { + "kind": "Conformance", + "name": "Equatable", + "printedName": "Equatable", + "usr": "s:SQ", + "mangledName": "$sSQ" + }, + { + "kind": "Conformance", + "name": "Hashable", + "printedName": "Hashable", + "usr": "s:SH", + "mangledName": "$sSH" + } + ] + }, { "kind": "TypeAlias", "name": "SentryBeforeBreadcrumbCallback", From 6d3defc02d1173f2d6567fa02bff072cce90a526 Mon Sep 17 00:00:00 2001 From: Itay Brenner Date: Tue, 4 Nov 2025 18:28:09 -0300 Subject: [PATCH 9/9] Update SentryAttachment docs --- Sources/Sentry/Public/SentryAttachment.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Sources/Sentry/Public/SentryAttachment.h b/Sources/Sentry/Public/SentryAttachment.h index 367c3750099..5e6cb0092e3 100644 --- a/Sources/Sentry/Public/SentryAttachment.h +++ b/Sources/Sentry/Public/SentryAttachment.h @@ -10,9 +10,24 @@ NS_ASSUME_NONNULL_BEGIN /** * Attachment Type + * + * This enum specifies the type of attachment. The attachment type is primarily used by downstream + * SDKs (such as sentry-godot) to distinguish between different attachment categories. + * Most applications using the SDK directly do not need to specify this, as the default + * @c kSentryAttachmentTypeEventAttachment is used for regular file attachments. + * + * + * See also: https://develop.sentry.dev/sdk/data-model/envelope-items/#attachment */ typedef NS_ENUM(NSInteger, SentryAttachmentType) { + /** + * Standard event attachment. This is the default type for user-provided attachments. + */ kSentryAttachmentTypeEventAttachment, + /** + * View hierarchy attachment. Automatically set by the SDK when capturing view hierarchy data. + * This type is primarily used by downstream SDKs. + */ kSentryAttachmentTypeViewHierarchy };