From 8516ee4a5299d6475ccbe1bdcb559d390d526c5d Mon Sep 17 00:00:00 2001 From: Fernand Lime Date: Wed, 15 Jul 2026 11:59:42 +0200 Subject: [PATCH] [iOS] Drop network error events of resources marked as dropped (#1338) On iOS, requests tracked by the JS layer are marked with the _dd.resource.drop_resource attribute so the native resourceEventMapper and actionEventMapper drop the duplicated native events. Error events emitted for the same failed requests carry the marker too but no errorEventMapper was registered, so they always reached Datadog and could not be filtered from application code. Register the missing mapper, mirroring the two existing ones, aligning iOS with the Android behavior where JS-tracked requests produce no native error events. Fixes #1338 --- .../Sources/DdSdkNativeInitialization.swift | 8 ++ packages/core/ios/Tests/DdSdkTests.swift | 18 ++++ packages/core/ios/Tests/RUMMocks.swift | 88 +++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/packages/core/ios/Sources/DdSdkNativeInitialization.swift b/packages/core/ios/Sources/DdSdkNativeInitialization.swift index c46ddd145..a2e51e805 100644 --- a/packages/core/ios/Sources/DdSdkNativeInitialization.swift +++ b/packages/core/ios/Sources/DdSdkNativeInitialization.swift @@ -231,6 +231,14 @@ public class DdSdkNativeInitialization: NSObject { } return actionEvent }, + errorEventMapper: { errorEvent in + if errorEvent.context?.contextInfo[InternalConfigurationAttributes.dropResource] + != nil + { + return nil + } + return errorEvent + }, onSessionStart: DdSdkSessionStartedListener.instance.rumSessionListener, customEndpoint: customRUMEndpointURL, trackMemoryWarnings: rumConfig.trackMemoryWarnings diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index bc13c4f98..53c60ad4b 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -1609,6 +1609,24 @@ class DdSdkTests: XCTestCase { XCTAssertNotNil(mappedEvent) } + func testDropsErrorMarkedAsDropped() throws { + let configuration: DdSdkConfiguration = .mockAny() + + let ddConfig = DdSdkNativeInitialization().buildRumConfiguration( + configuration: configuration + ) + + let errorEventMapper = try XCTUnwrap(ddConfig.errorEventMapper) + + let mockDroppedErrorEvent = RUMErrorEvent.mockRandomDropped() + let mappedDroppedEvent = errorEventMapper(mockDroppedErrorEvent) + XCTAssertNil(mappedDroppedEvent) + + let mockErrorEvent = RUMErrorEvent.mockRandom() + let mappedEvent = errorEventMapper(mockErrorEvent) + XCTAssertNotNil(mappedEvent) + } + func testReactNativeThreadMonitorsRunOnBridge() throws { let bridge = DispatchQueueMock() let mockJSRefreshRateMonitor = MockJSRefreshRateMonitor() diff --git a/packages/core/ios/Tests/RUMMocks.swift b/packages/core/ios/Tests/RUMMocks.swift index c07f0fa7f..fb6f2eced 100644 --- a/packages/core/ios/Tests/RUMMocks.swift +++ b/packages/core/ios/Tests/RUMMocks.swift @@ -514,6 +514,94 @@ extension RUMActionEvent: RandomMockable { } } +extension RUMErrorEvent: RandomMockable { + static func mockRandomDropped() -> RUMErrorEvent { + return RUMErrorEvent( + dd: .init( + browserSdkVersion: nil, + configuration: nil, + session: .init(plan: .plan1, sessionPrecondition: nil) + ), + application: .init(id: .mockRandom()), + buildId: nil, + buildVersion: nil, + ciTest: nil, + connectivity: .mockRandom(), + container: nil, + context: .init(contextInfo: ["_dd.resource.drop_resource": true] ), + date: .mockRandom(), + device: .mockRandom(), + display: nil, + error: .init( + isCrash: false, + message: .mockRandom(), + source: .network, + stack: .mockRandom() + ), + os: .mockRandom(), + service: .mockRandom(), + session: .init( + hasReplay: nil, + id: .mockRandom(), + type: .user + ), + source: .ios, + synthetics: nil, + usr: .mockRandom(), + version: .mockRandom(), + view: .init( + id: .mockRandom(), + inForeground: .random(), + referrer: .mockRandom(), + url: .mockRandom() + ) + ) + } + + static func mockRandom() -> RUMErrorEvent { + return RUMErrorEvent( + dd: .init( + browserSdkVersion: nil, + configuration: nil, + session: .init(plan: .plan1, sessionPrecondition: nil) + ), + application: .init(id: .mockRandom()), + buildId: nil, + buildVersion: nil, + ciTest: nil, + connectivity: .mockRandom(), + container: nil, + context: nil, + date: .mockRandom(), + device: .mockRandom(), + display: nil, + error: .init( + isCrash: false, + message: .mockRandom(), + source: .network, + stack: .mockRandom() + ), + os: .mockRandom(), + service: .mockRandom(), + session: .init( + hasReplay: nil, + id: .mockRandom(), + type: .user + ), + source: .ios, + synthetics: nil, + usr: .mockRandom(), + version: .mockRandom(), + view: .init( + id: .mockRandom(), + inForeground: .random(), + referrer: .mockRandom(), + url: .mockRandom() + ) + ) + } +} + // MARK: - URLRequest Mocks extension URLRequest {