diff --git a/Datadog/Datadog.xcodeproj/project.pbxproj b/Datadog/Datadog.xcodeproj/project.pbxproj index a7dbc98c48..1e48501fab 100644 --- a/Datadog/Datadog.xcodeproj/project.pbxproj +++ b/Datadog/Datadog.xcodeproj/project.pbxproj @@ -644,7 +644,6 @@ 61BB2B1B244A185D009F3F56 /* PerformancePreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61BB2B1A244A185D009F3F56 /* PerformancePreset.swift */; }; 61BBD19724ED50040023E65F /* DatadogConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61BBD19624ED50040023E65F /* DatadogConfigurationTests.swift */; }; 61C363802436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C3637F2436164B00C4D4E6 /* ObjcExceptionHandlerTests.swift */; }; - 61C4534A2C0A0BBF00CC4C17 /* TelemetryInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C453492C0A0BBF00CC4C17 /* TelemetryInterceptorTests.swift */; }; 61C5A89624509BF600DA608C /* TracerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C5A89524509BF600DA608C /* TracerTests.swift */; }; 61C713A32A3B78F900FA735A /* RUMMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C713A02A3B78F900FA735A /* RUMMonitorProtocol.swift */; }; 61C713A52A3B78F900FA735A /* RUMMonitorProtocol+Internal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C713A12A3B78F900FA735A /* RUMMonitorProtocol+Internal.swift */; }; @@ -681,7 +680,6 @@ 61DA8CB828647A500074A606 /* InternalLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61DA8CB728647A500074A606 /* InternalLoggerTests.swift */; }; 61DB33B225DEDFC200F7EA71 /* CustomObjcViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 61DB33B125DEDFC200F7EA71 /* CustomObjcViewController.m */; }; 61DCC8472C05CD0000CB59E5 /* SessionEndedMetricControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61DCC8462C05CD0000CB59E5 /* SessionEndedMetricControllerTests.swift */; }; - 61DCC84E2C071DCD00CB59E5 /* TelemetryInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61DCC84D2C071DCD00CB59E5 /* TelemetryInterceptor.swift */; }; 61E262D42EB2592C0041E70F /* DatadogFlags.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BA8C2ED2E784B3C00B1DA80 /* DatadogFlags.framework */; }; 61E5333824B84EE2003D6C4E /* DebugRUMViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E5333724B84EE2003D6C4E /* DebugRUMViewController.swift */; }; 61ED39D426C2A36B002C0F26 /* DataUploadStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61ED39D326C2A36B002C0F26 /* DataUploadStatus.swift */; }; @@ -859,12 +857,16 @@ D21A94F22B8397CA00AC4256 /* WebViewMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21A94F12B8397CA00AC4256 /* WebViewMessage.swift */; }; D21AE6BC29E5EDAF0064BF29 /* TelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21AE6BB29E5EDAF0064BF29 /* TelemetryTests.swift */; }; D21C26C528A3B49C005DD405 /* FeatureStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21C26C428A3B49C005DD405 /* FeatureStorage.swift */; }; - D21C26D128A64599005DD405 /* MessageBusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21C26D028A64599005DD405 /* MessageBusTests.swift */; }; + D21C26D128A64599005DD405 /* CoreMessageBusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D21C26D028A64599005DD405 /* CoreMessageBusTests.swift */; }; D22442C52CA301DA002E71E4 /* UIColor+SessionReplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22442C42CA301DA002E71E4 /* UIColor+SessionReplay.swift */; }; D224430429E9588100274EC7 /* TelemetryReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D214DAA729E54CB4004D0AE8 /* TelemetryReceiver.swift */; }; - D224430629E95C2C00274EC7 /* MessageBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D214DAA429E072D7004D0AE8 /* MessageBus.swift */; }; + D224430629E95C2C00274EC7 /* CoreMessageBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D214DAA429E072D7004D0AE8 /* CoreMessageBus.swift */; }; D224430D29E95D6700274EC7 /* CrashReportReceiverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224430C29E95D6600274EC7 /* CrashReportReceiverTests.swift */; }; D224430F29E9779F00274EC7 /* TelemetryReceiverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D248ED4728081B9B00B315B4 /* TelemetryReceiverTests.swift */; }; + D224DFA82FAB8C4B000AED53 /* MessageBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224DFA72FAB8C4B000AED53 /* MessageBus.swift */; }; + D224DFAA2FAB9DCE000AED53 /* MessageBusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224DFA92FAB9DCE000AED53 /* MessageBusTests.swift */; }; + D224DFAE2FACFA43000AED53 /* RUMDataModels+BusMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224DFAD2FACFA43000AED53 /* RUMDataModels+BusMessage.swift */; }; + D224DFB02FB1BBCA000AED53 /* WebViewBusMessages.swift in Sources */ = {isa = PBXBuildFile; fileRef = D224DFAF2FB1BBCA000AED53 /* WebViewBusMessages.swift */; }; D22689B92EB12C3100875E44 /* Filters in Frameworks */ = {isa = PBXBuildFile; productRef = D22689B82EB12C3100875E44 /* Filters */; }; D22689BB2EB12C3100875E44 /* Recording in Frameworks */ = {isa = PBXBuildFile; productRef = D22689BA2EB12C3100875E44 /* Recording */; }; D22689C32EB12D3D00875E44 /* KSCrashPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22689C22EB12D3D00875E44 /* KSCrashPlugin.swift */; }; @@ -1208,7 +1210,6 @@ D2C5D52B2B84F6AB00B63F36 /* WebViewRecordReceiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C5D52A2B84F6AB00B63F36 /* WebViewRecordReceiver.swift */; }; D2C5D52D2B84F6D800B63F36 /* WebViewRecordReceiverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C5D52C2B84F6D800B63F36 /* WebViewRecordReceiverTests.swift */; }; D2C7E3AB28F97DCF0023B2CC /* BatteryStatusPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2C7E3AA28F97DCF0023B2CC /* BatteryStatusPublisherTests.swift */; }; - D2CC33DB2FBCC81D00377194 /* MessageBus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2CC33DA2FBCC81D00377194 /* MessageBus.swift */; }; D2D0E1F22E3CE59700BA4113 /* mach_profiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D2D0E1F12E3CE59700BA4113 /* mach_profiler.cpp */; }; D2D0E1F82E3CE73700BA4113 /* mach_sampling_profiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D2D0E1F72E3CE73700BA4113 /* mach_sampling_profiler.cpp */; }; D2D0E2072E3CEF1200BA4113 /* MachSamplingProfilerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D0E2022E3CEF1200BA4113 /* MachSamplingProfilerTests.swift */; }; @@ -2467,7 +2468,6 @@ 61C3E63824BF19B4008053F2 /* RUMContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMContext.swift; sourceTree = ""; }; 61C3E63A24BF1A4B008053F2 /* RUMCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMCommand.swift; sourceTree = ""; }; 61C3E63D24BF1B91008053F2 /* RUMApplicationScope.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMApplicationScope.swift; sourceTree = ""; }; - 61C453492C0A0BBF00CC4C17 /* TelemetryInterceptorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryInterceptorTests.swift; sourceTree = ""; }; 61C5A87824509A0C00DA608C /* DDSpan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DDSpan.swift; sourceTree = ""; }; 61C5A87924509A0C00DA608C /* DDNoOps.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DDNoOps.swift; sourceTree = ""; }; 61C5A87C24509A0C00DA608C /* Casting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Casting.swift; sourceTree = ""; }; @@ -2517,7 +2517,6 @@ 61DB33B125DEDFC200F7EA71 /* CustomObjcViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CustomObjcViewController.m; sourceTree = ""; }; 61DCC8462C05CD0000CB59E5 /* SessionEndedMetricControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionEndedMetricControllerTests.swift; sourceTree = ""; }; 61DCC8492C05D4D600CB59E5 /* RUMSessionEndedMetricIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMSessionEndedMetricIntegrationTests.swift; sourceTree = ""; }; - 61DCC84D2C071DCD00CB59E5 /* TelemetryInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryInterceptor.swift; sourceTree = ""; }; 61DE333525C8278A008E3EC2 /* CrashReportingPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReportingPlugin.swift; sourceTree = ""; }; 61E141EE2D42BC74008C8851 /* RUMViewEndedMetricIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMViewEndedMetricIntegrationTests.swift; sourceTree = ""; }; 61E45BCE2450A6EC00F2C652 /* TraceIDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraceIDTests.swift; sourceTree = ""; }; @@ -2713,7 +2712,7 @@ D20FD9D52ACC0934004D3569 /* WebLogIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebLogIntegrationTests.swift; sourceTree = ""; }; D21331C02D132F0600E4A6A1 /* SwiftUIWireframesBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIWireframesBuilderTests.swift; sourceTree = ""; }; D213532F270CA722000315AD /* DataCompressionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataCompressionTests.swift; sourceTree = ""; }; - D214DAA429E072D7004D0AE8 /* MessageBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBus.swift; sourceTree = ""; }; + D214DAA429E072D7004D0AE8 /* CoreMessageBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreMessageBus.swift; sourceTree = ""; }; D214DAA729E54CB4004D0AE8 /* TelemetryReceiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryReceiver.swift; sourceTree = ""; }; D215ED6A29D2E1080046B721 /* ErrorMessageReceiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessageReceiver.swift; sourceTree = ""; }; D215F0932F6D5DE000E86466 /* ThirdPartyDelegateProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThirdPartyDelegateProxy.swift; sourceTree = ""; }; @@ -2737,11 +2736,15 @@ D21A94F12B8397CA00AC4256 /* WebViewMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewMessage.swift; sourceTree = ""; }; D21AE6BB29E5EDAF0064BF29 /* TelemetryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryTests.swift; sourceTree = ""; }; D21C26C428A3B49C005DD405 /* FeatureStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureStorage.swift; sourceTree = ""; }; - D21C26D028A64599005DD405 /* MessageBusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBusTests.swift; sourceTree = ""; }; + D21C26D028A64599005DD405 /* CoreMessageBusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreMessageBusTests.swift; sourceTree = ""; }; D21C26EA28AFA11E005DD405 /* LogMessageReceiverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogMessageReceiverTests.swift; sourceTree = ""; }; D21C26ED28AFB65B005DD405 /* ErrorMessageReceiverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessageReceiverTests.swift; sourceTree = ""; }; D22442C42CA301DA002E71E4 /* UIColor+SessionReplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+SessionReplay.swift"; sourceTree = ""; }; D224430C29E95D6600274EC7 /* CrashReportReceiverTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CrashReportReceiverTests.swift; sourceTree = ""; }; + D224DFA72FAB8C4B000AED53 /* MessageBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBus.swift; sourceTree = ""; }; + D224DFA92FAB9DCE000AED53 /* MessageBusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBusTests.swift; sourceTree = ""; }; + D224DFAD2FACFA43000AED53 /* RUMDataModels+BusMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RUMDataModels+BusMessage.swift"; sourceTree = ""; }; + D224DFAF2FB1BBCA000AED53 /* WebViewBusMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewBusMessages.swift; sourceTree = ""; }; D22689C22EB12D3D00875E44 /* KSCrashPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KSCrashPlugin.swift; sourceTree = ""; }; D22689C62EB2151F00875E44 /* KSCrashPluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KSCrashPluginTests.swift; sourceTree = ""; }; D2268AD12EB4DA4100875E44 /* DatadogTypeSafeFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatadogTypeSafeFilter.swift; sourceTree = ""; }; @@ -2955,7 +2958,6 @@ D2C7E3AA28F97DCF0023B2CC /* BatteryStatusPublisherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryStatusPublisherTests.swift; sourceTree = ""; }; D2CBC26A294383F200134409 /* WebViewEventReceiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewEventReceiver.swift; sourceTree = ""; }; D2CBC26D294395A300134409 /* RUMContextAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMContextAttributes.swift; sourceTree = ""; }; - D2CC33DA2FBCC81D00377194 /* MessageBus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBus.swift; sourceTree = ""; }; D2D0E1F12E3CE59700BA4113 /* mach_profiler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = mach_profiler.cpp; sourceTree = ""; }; D2D0E1F72E3CE73700BA4113 /* mach_sampling_profiler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = mach_sampling_profiler.cpp; sourceTree = ""; }; D2D0E2012E3CEF1200BA4113 /* MachProfilerCAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MachProfilerCAPITests.swift; sourceTree = ""; }; @@ -4530,7 +4532,7 @@ isa = PBXGroup; children = ( D2B3F04C282A85FD00C2B5EE /* DatadogCore.swift */, - D214DAA429E072D7004D0AE8 /* MessageBus.swift */, + D214DAA429E072D7004D0AE8 /* CoreMessageBus.swift */, D2EFA866286DA82700F1FAA6 /* Context */, 61133BA62423979B00786299 /* Storage */, 6128F56C2BA223A100D35B08 /* DataStore */, @@ -5018,7 +5020,6 @@ D236BE2729520FED00676E67 /* CrashReportReceiver.swift */, D215ED6A29D2E1080046B721 /* ErrorMessageReceiver.swift */, D214DAA729E54CB4004D0AE8 /* TelemetryReceiver.swift */, - 61DCC84D2C071DCD00CB59E5 /* TelemetryInterceptor.swift */, D2D748222DC0FF7E00C61353 /* FatalErrorContextNotifier.swift */, 5B1D02842E8EB78600AB2391 /* FlagEvaluationReceiver.swift */, ); @@ -5352,6 +5353,7 @@ D28FB6952DB7D3F000CD76D0 /* RUMDataModels.swift */, D2D9A9DB2DBFD507005DB31D /* RUMPayloadMessages.swift */, D2E8A8E62DCBBA5100CF7C63 /* RUMCoreContext.swift */, + D224DFAD2FACFA43000AED53 /* RUMDataModels+BusMessage.swift */, ); path = RUM; sourceTree = ""; @@ -6172,7 +6174,7 @@ children = ( D23039C1298D5235001A1FA3 /* FeatureMessageReceiver.swift */, D23039C2298D5235001A1FA3 /* FeatureMessage.swift */, - D2CC33DA2FBCC81D00377194 /* MessageBus.swift */, + D224DFA72FAB8C4B000AED53 /* MessageBus.swift */, ); path = MessageBus; sourceTree = ""; @@ -6258,7 +6260,6 @@ D21C26ED28AFB65B005DD405 /* ErrorMessageReceiverTests.swift */, 9E53889B2773C4B300A7DC42 /* WebViewEventReceiverTests.swift */, D248ED4728081B9B00B315B4 /* TelemetryReceiverTests.swift */, - 61C453492C0A0BBF00CC4C17 /* TelemetryInterceptorTests.swift */, 5B1D02932E8ED6BE00AB2391 /* FlagEvaluationReceiverTests.swift */, ); path = Integrations; @@ -6321,6 +6322,7 @@ isa = PBXGroup; children = ( D21A94F12B8397CA00AC4256 /* WebViewMessage.swift */, + D224DFAF2FB1BBCA000AED53 /* WebViewBusMessages.swift */, ); path = WebViewTracking; sourceTree = ""; @@ -6565,6 +6567,7 @@ isa = PBXGroup; children = ( D2DA23A0298D58F400C6C7E6 /* FeatureMessageReceiverTests.swift */, + D224DFA92FAB9DCE000AED53 /* MessageBusTests.swift */, ); path = MessageBus; sourceTree = ""; @@ -6812,7 +6815,7 @@ 614B78EA296D7B63009C6B92 /* DatadogCoreTests.swift */, 6167E70D2B83502200C3CA2D /* DatadogCore+FeatureDirectoriesTests.swift */, 613F9C172BAC3527007C7606 /* DatadogCore+FeatureDataStoreTests.swift */, - D21C26D028A64599005DD405 /* MessageBusTests.swift */, + D21C26D028A64599005DD405 /* CoreMessageBusTests.swift */, ); path = DatadogCore; sourceTree = ""; @@ -7969,7 +7972,7 @@ 6128F5742BA3280300D35B08 /* DataStoreFileReader.swift in Sources */, 11F59BC22EAE2180009F8579 /* LaunchInfoPublisher.swift in Sources */, D2553829288F0B2400727FAD /* LowPowerModePublisher.swift in Sources */, - D224430629E95C2C00274EC7 /* MessageBus.swift in Sources */, + D224430629E95C2C00274EC7 /* CoreMessageBus.swift in Sources */, 61F930BE2BA1ACAC005F0EE2 /* Storage+TLV.swift in Sources */, 6128F5772BA32DE500D35B08 /* DataStoreFileWriter.swift in Sources */, 6139CD712589FAFD007E8BB7 /* Retrying.swift in Sources */, @@ -8163,7 +8166,7 @@ 61345613244756E300E7DA6B /* PerformancePresetTests.swift in Sources */, D28F836C29C9E7A300EF8EA2 /* TracingURLSessionHandlerTests.swift in Sources */, F603F12B2CAEA4FA0088E6B7 /* DDInternalLoggerTests.swift in Sources */, - D21C26D128A64599005DD405 /* MessageBusTests.swift in Sources */, + D21C26D128A64599005DD405 /* CoreMessageBusTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -8573,7 +8576,6 @@ D21A94F22B8397CA00AC4256 /* WebViewMessage.swift in Sources */, D23039EC298D5236001A1FA3 /* LaunchInfo.swift in Sources */, 6175C3512BCE66DB006FAAB0 /* TraceContext.swift in Sources */, - D2CC33DB2FBCC81D00377194 /* MessageBus.swift in Sources */, D227A0A42C7622EA00C83324 /* BenchmarkProfiler.swift in Sources */, D23039EE298D5236001A1FA3 /* FeatureMessageReceiver.swift in Sources */, D23039DE298D5235001A1FA3 /* Writer.swift in Sources */, @@ -8638,6 +8640,7 @@ 3CA8525F2BF2073800B52CBA /* TraceContextInjection.swift in Sources */, D23039F6298D5236001A1FA3 /* Attributes.swift in Sources */, D20731CB29A52E6000ECBF94 /* Sampler.swift in Sources */, + D224DFA82FAB8C4B000AED53 /* MessageBus.swift in Sources */, D2EBEE2029BA160F00B15732 /* TracePropagationHeadersWriter.swift in Sources */, 267134902D688D280048CB54 /* AccountInfo.swift in Sources */, D23039EF298D5236001A1FA3 /* FeatureMessage.swift in Sources */, @@ -8649,6 +8652,7 @@ D23039E0298D5235001A1FA3 /* DatadogCoreProtocol.swift in Sources */, D27465812E7B1C4D00C47FE2 /* ProfilingMessages.swift in Sources */, 5BFDD7A92E28FDD3009A2CEE /* RUMWebViewContext.swift in Sources */, + D224DFAE2FACFA43000AED53 /* RUMDataModels+BusMessage.swift in Sources */, D23039FD298D5236001A1FA3 /* DataCompression.swift in Sources */, D2C179E12DD2388800556F68 /* TraceCoreContext.swift in Sources */, B3E46CAE2D91B40000BABF66 /* NetworkContext.swift in Sources */, @@ -8668,6 +8672,7 @@ E996B2C5B9B268490842F924 /* HeatmapAttributes.swift in Sources */, 9D0C56F6A6472E38F662D6D7 /* HeatmapIdentifier.swift in Sources */, 13124CCC91AF2DC5EC18DDA6 /* HeatmapIdentifierRegistry.swift in Sources */, + D224DFB02FB1BBCA000AED53 /* WebViewBusMessages.swift in Sources */, A01D384CBDE4FFDC49CCD424 /* HeatmapIdentifierRegistryFeature.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -8990,7 +8995,6 @@ 11A2F24A2E70CC08006EDC52 /* FrameInfoProvider.swift in Sources */, 11A2F24B2E70CC08006EDC52 /* MediaTimeProvider.swift in Sources */, D29A9F5229DD85BB005C54A4 /* RUMUUIDGenerator.swift in Sources */, - 61DCC84E2C071DCD00CB59E5 /* TelemetryInterceptor.swift in Sources */, 4062CCAEB02C9EEF6761F4F8 /* HeatmapIdentifierStore.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -9002,7 +9006,6 @@ 6188697C2A4376F700E8996B /* RUMConfigurationTests.swift in Sources */, 61DCC8472C05CD0000CB59E5 /* SessionEndedMetricControllerTests.swift in Sources */, D29A9FA629DDB483005C54A4 /* RUMOffViewEventsHandlingRuleTests.swift in Sources */, - 61C4534A2C0A0BBF00CC4C17 /* TelemetryInterceptorTests.swift in Sources */, D29A9FBD29DDB483005C54A4 /* RUMSessionScopeTests.swift in Sources */, 0904F9F62EE1DA6800ED9A22 /* UIKitExtensionsTests.swift in Sources */, 3C4CF9982C47CC91006DE1C0 /* MemoryWarningMonitorTests.swift in Sources */, @@ -9102,6 +9105,7 @@ D21AE6BC29E5EDAF0064BF29 /* TelemetryTests.swift in Sources */, D2DA23A3298D58F400C6C7E6 /* AnyEncodableTests.swift in Sources */, 3CCECDAF2BC688120013C125 /* SpanIDGeneratorTests.swift in Sources */, + D224DFAA2FAB9DCE000AED53 /* MessageBusTests.swift in Sources */, D263BCB429DB014900FA0E21 /* FixedWidthInteger+ConvenienceTests.swift in Sources */, D233E7D22E4627D900E7CFDE /* MultipartFormDataTests.swift in Sources */, 09F8243A2FB48F5B005D1DCE /* WebViewSessionRolloverHandlerTests.swift in Sources */, diff --git a/DatadogCore/Sources/Core/MessageBus.swift b/DatadogCore/Sources/Core/CoreMessageBus.swift similarity index 52% rename from DatadogCore/Sources/Core/MessageBus.swift rename to DatadogCore/Sources/Core/CoreMessageBus.swift index e8d687de5d..350371b4e1 100644 --- a/DatadogCore/Sources/Core/MessageBus.swift +++ b/DatadogCore/Sources/Core/CoreMessageBus.swift @@ -10,7 +10,7 @@ import DatadogInternal /// The message-bus sends messages to a set of registered receivers. /// /// The bus dispatches messages on a serial queue. -internal final class MessageBus { +internal final class CoreMessageBus: @unchecked Sendable { /// The message bus GDC queue. let queue = DispatchQueue( label: "com.datadoghq.ios-sdk-message-bus", @@ -27,6 +27,25 @@ internal final class MessageBus { /// The bus **must** be accessed within the queue. private var bus: [String: FeatureMessageReceiver] = [:] + /// A closure that delivers a `BusMessage` to a single subscriber. + /// + /// Captures the receiver strongly and performs the runtime cast from `Any` + /// to the receiver's expected `Message` type. + private typealias Dispatch = (Any, DatadogCoreProtocol) -> Void + + /// Typed `BusMessageReceiver` subscribers grouped by `BusMessage.key`. + /// + /// The outer key is the `BusMessage.key` of the message kind a subscriber + /// is registered for; the inner key is the receiver's object identity. The + /// keyed layout means dispatch only iterates receivers for the matching + /// message kind instead of the full subscriber set. + /// + /// Each `Dispatch` captures its receiver strongly, so the bus retains + /// subscribers until they are explicitly removed via `unsubscribe(receiver:)`. + /// + /// Must be accessed within the queue. + private var receivers: [String: [ObjectIdentifier: Dispatch]] = [:] + /// The current configuration. /// /// The message-bus wil accumulate configuration by merge. A message @@ -42,11 +61,18 @@ internal final class MessageBus { /// - Parameter configurationDispatchTime: The delay to dispatch the /// configuration telemetry init(configurationDispatchTime: DispatchTimeInterval = .seconds(5)) { - queue.asyncAfter(deadline: .now() + configurationDispatchTime) { - guard let core = self.core, let configuration = self.configuration else { + queue.asyncAfter(deadline: .now() + configurationDispatchTime) { [weak self] in + guard let self = self, let core = self.core, let configuration = self.configuration else { return } + // Dispatch via typed bus to TelemetryMessage subscribers. + if let bucket = self.receivers[TelemetryMessage.key] { + let message = TelemetryMessage.configuration(configuration) + bucket.values.forEach { dispatch in dispatch(message, core) } + } + + // Dispatch via legacy bus for receivers still on the legacy bus. self.bus.values.forEach { $0.receive(message: .telemetry(.configuration(configuration)), from: core) } @@ -125,7 +151,69 @@ internal final class MessageBus { } } -extension MessageBus: Flushable { +extension CoreMessageBus: MessageBus { + /// Adds `receiver` to the bucket for `Receiver.Message.key`. + /// + /// The receiver is retained by the bus (via the captured closure) until + /// `unsubscribe(receiver:)` is called. Re-subscribing the same instance for + /// the same message kind replaces the previous subscription — entries are + /// keyed by object identity. + func subscribe(receiver: Receiver) where Receiver: BusMessageReceiver { + queue.async { + let id = ObjectIdentifier(receiver) + self.receivers[Receiver.Message.key, default: [:]][id] = { message, core in + guard let typed = message as? Receiver.Message else { + return + } + receiver.receive(message: typed, from: core) + } + } + } + + /// Removes `receiver` from the bucket for `Receiver.Message.key`. + /// + /// No-op if `receiver` is not currently subscribed. Empty buckets are + /// pruned to keep the registry tidy. + func unsubscribe(receiver: Receiver) where Receiver: BusMessageReceiver { + queue.async { + let key = Receiver.Message.key + let id = ObjectIdentifier(receiver) + self.receivers[key]?.removeValue(forKey: id) + if self.receivers[key]?.isEmpty == true { + self.receivers.removeValue(forKey: key) + } + } + } + + /// Publishes `message` to every receiver in the bucket for `Message.key`. + /// + /// Configuration telemetry messages are intercepted and accumulated for deferred + /// batch dispatch; they are never routed to subscribers immediately. + /// + /// `fallback` is invoked when the bus has no core, or when the bucket is + /// empty. Delivery is dispatched on the bus's serial queue. + func send(message: Message, else fallback: @escaping () -> Void) where Message: BusMessage { + // Intercept configuration telemetry for deferred accumulated dispatch. + if let telemetry = message as? TelemetryMessage, case .configuration(let config) = telemetry { + save(configuration: config) + return + } + + queue.async { + guard let core = self.core else { + return fallback() + } + guard let bucket = self.receivers[Message.key], !bucket.isEmpty else { + return fallback() + } + bucket.values.forEach { dispatch in + dispatch(message, core) + } + } + } +} + +extension CoreMessageBus: Flushable { /// Awaits completion of all asynchronous operations. /// /// **blocks the caller thread** diff --git a/DatadogCore/Sources/Core/DatadogCore.swift b/DatadogCore/Sources/Core/DatadogCore.swift index c7b8738e2b..24a376b67c 100644 --- a/DatadogCore/Sources/Core/DatadogCore.swift +++ b/DatadogCore/Sources/Core/DatadogCore.swift @@ -53,7 +53,7 @@ internal final class DatadogCore { let applicationVersionPublisher: ApplicationVersionPublisher /// The message-bus instance. - let bus = MessageBus() + let bus = CoreMessageBus() /// Registry for Features. @ReadWriteLock @@ -119,9 +119,9 @@ internal final class DatadogCore { // the bus will keep a weak ref to the core. bus.connect(core: self) - // forward any context change on the message-bus + // forward any context change on the typed message-bus self.contextProvider.publish { [weak self] context in - self?.send(message: .context(context)) + self?.bus.send(message: context) } } @@ -264,8 +264,9 @@ internal final class DatadogCore { /// - key: The key associated with the receiver. private func add(messageReceiver: FeatureMessageReceiver, forKey key: String) { bus.connect(messageReceiver, forKey: key) + // Push current context to typed-bus DatadogContext subscribers. contextProvider.read { context in - self.bus.queue.async { messageReceiver.receive(message: .context(context), from: self) } + self.bus.send(message: context) } } @@ -323,6 +324,8 @@ internal final class DatadogCore { } extension DatadogCore: DatadogCoreProtocol { + var messageBus: MessageBus { bus } + /// Registers a Feature instance. /// /// A Feature collects and transfers data to a Datadog Product (e.g. Logs, RUM, ...). A registered Feature can diff --git a/DatadogCore/Sources/Extensions/ContextSharing/ContextSharingTransformer.swift b/DatadogCore/Sources/Extensions/ContextSharing/ContextSharingTransformer.swift index bb9c3f3c0e..bf37f3b489 100644 --- a/DatadogCore/Sources/Extensions/ContextSharing/ContextSharingTransformer.swift +++ b/DatadogCore/Sources/Extensions/ContextSharing/ContextSharingTransformer.swift @@ -6,24 +6,16 @@ import DatadogInternal -internal final class ContextSharingTransformer: FeatureMessageReceiver, ContextValuePublisher { +internal final class ContextSharingTransformer: BusMessageReceiver, ContextValuePublisher { @ReadWriteLock private var sharedContext: SharedContext? = nil @ReadWriteLock private var receiver: ContextValueReceiver? = nil - // MARK: - FeatureMessageReceiver - - func receive(message: FeatureMessage, from core: DatadogCoreProtocol) -> Bool { - switch message { - case .context(let context): - let newContext = SharedContext(datadogContext: context) - sharedContext = newContext - receiver?(newContext) - return true - default: - return false - } + func receive(message context: DatadogContext, from core: DatadogCoreProtocol) { + let newContext = SharedContext(datadogContext: context) + sharedContext = newContext + receiver?(newContext) } // MARK: - ContextValuePublisher diff --git a/DatadogCore/Sources/Extensions/CrossPlatformExtension.swift b/DatadogCore/Sources/Extensions/CrossPlatformExtension.swift index 15bad05648..a487f6e187 100644 --- a/DatadogCore/Sources/Extensions/CrossPlatformExtension.swift +++ b/DatadogCore/Sources/Extensions/CrossPlatformExtension.swift @@ -29,7 +29,9 @@ public final class CrossPlatformExtension: NSObject { if Self.contextSharingTransformer == nil { let core = CoreRegistry.default let contextSharingTransformer = ContextSharingTransformer() - try? core.register(feature: ContextSharingFeature(messageReceiver: contextSharingTransformer)) + // Subscribe before registration so initial context push is received: + core.messageBus.subscribe(receiver: contextSharingTransformer) + try? core.register(feature: ContextSharingFeature(messageReceiver: NOPFeatureMessageReceiver())) Self.contextSharingTransformer = contextSharingTransformer } contextSharingTransformer?.publish(to: toSharedContext) diff --git a/DatadogCore/Tests/Datadog/CrashReporting/CrashContext/CrashContextProviderTests.swift b/DatadogCore/Tests/Datadog/CrashReporting/CrashContext/CrashContextProviderTests.swift index be3805ef19..a266ea156d 100644 --- a/DatadogCore/Tests/Datadog/CrashReporting/CrashContext/CrashContextProviderTests.swift +++ b/DatadogCore/Tests/Datadog/CrashReporting/CrashContext/CrashContextProviderTests.swift @@ -20,7 +20,21 @@ import TestUtilities /// This suite tests if `CrashContextProvider` gets updated by different SDK components, each updating /// separate part of the `CrashContext` information. class CrashContextProviderTests: XCTestCase { - private let provider = CrashContextCoreProvider() + private var provider: CrashContextCoreProvider! // swiftlint:disable:this implicitly_unwrapped_optional + private var core: PassthroughCoreMock! // swiftlint:disable:this implicitly_unwrapped_optional + + override func setUp() { + super.setUp() + core = PassthroughCoreMock() + provider = CrashContextCoreProvider() + provider.subscribe(to: core.messageBus) + } + + override func tearDown() { + core = nil + provider = nil + super.tearDown() + } // MARK: - Receiving SDK Context @@ -32,7 +46,7 @@ class CrashContextProviderTests: XCTestCase { let sdkContext: DatadogContext = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(sdkContext), from: NOPDatadogCore())) + core.messageBus.send(message: sdkContext) // Then provider.flush() @@ -49,8 +63,8 @@ class CrashContextProviderTests: XCTestCase { let nextSDKContext: DatadogContext = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(.mockRandom()), from: NOPDatadogCore())) // receive initial - XCTAssertTrue(provider.receive(message: .context(nextSDKContext), from: NOPDatadogCore())) // receive next + core.messageBus.send(message: DatadogContext.mockRandom()) // receive initial + core.messageBus.send(message: nextSDKContext) // receive next // Then provider.flush() @@ -70,8 +84,8 @@ class CrashContextProviderTests: XCTestCase { let rumView: RUMViewEvent = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(sdkContext), from: NOPDatadogCore())) - XCTAssertTrue(provider.receive(message: .payload(rumView), from: NOPDatadogCore())) + core.messageBus.send(message: sdkContext) + core.messageBus.send(message: rumView) // Then provider.flush() @@ -90,9 +104,9 @@ class CrashContextProviderTests: XCTestCase { let nextSDKContext: DatadogContext = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(.mockRandom()), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(rumView), from: NOPDatadogCore())) - XCTAssertTrue(provider.receive(message: .context(nextSDKContext), from: NOPDatadogCore())) + core.messageBus.send(message: DatadogContext.mockRandom()) // receive initial SDK context + core.messageBus.send(message: rumView) + core.messageBus.send(message: nextSDKContext) // Then provider.flush() @@ -113,9 +127,9 @@ class CrashContextProviderTests: XCTestCase { let rumView: RUMViewEvent = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(sdkContext), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(rumView), from: NOPDatadogCore())) - XCTAssertTrue(provider.receive(message: .payload(RUMPayloadMessages.viewReset), from: NOPDatadogCore())) + core.messageBus.send(message: sdkContext) // receive initial SDK context + core.messageBus.send(message: rumView) + core.messageBus.send(message: RUMViewReset()) // Then provider.flush() @@ -134,10 +148,10 @@ class CrashContextProviderTests: XCTestCase { let nextSDKContext: DatadogContext = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(.mockRandom()), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(rumView), from: NOPDatadogCore())) - XCTAssertTrue(provider.receive(message: .payload(RUMPayloadMessages.viewReset), from: NOPDatadogCore())) - XCTAssertTrue(provider.receive(message: .context(nextSDKContext), from: NOPDatadogCore())) + core.messageBus.send(message: DatadogContext.mockRandom()) // receive initial SDK context + core.messageBus.send(message: rumView) + core.messageBus.send(message: RUMViewReset()) + core.messageBus.send(message: nextSDKContext) // Then provider.flush() @@ -158,8 +172,8 @@ class CrashContextProviderTests: XCTestCase { let rumSessionState: RUMSessionState = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(sdkContext), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(rumSessionState), from: NOPDatadogCore())) + core.messageBus.send(message: sdkContext) // receive initial SDK context + core.messageBus.send(message: rumSessionState) // Then provider.flush() @@ -178,9 +192,9 @@ class CrashContextProviderTests: XCTestCase { let nextSDKContext: DatadogContext = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(.mockRandom()), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(rumSessionState), from: NOPDatadogCore())) - XCTAssertTrue(provider.receive(message: .context(nextSDKContext), from: NOPDatadogCore())) + core.messageBus.send(message: DatadogContext.mockRandom()) // receive initial SDK context + core.messageBus.send(message: rumSessionState) + core.messageBus.send(message: nextSDKContext) // Then provider.flush() @@ -201,8 +215,8 @@ class CrashContextProviderTests: XCTestCase { let rumAttributes: RUMEventAttributes = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(sdkContext), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(rumAttributes), from: NOPDatadogCore())) + core.messageBus.send(message: sdkContext) // receive initial SDK context + core.messageBus.send(message: rumAttributes) // Then provider.flush() @@ -221,9 +235,9 @@ class CrashContextProviderTests: XCTestCase { let nextSDKContext: DatadogContext = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(.mockRandom()), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(rumAttributes), from: NOPDatadogCore())) - XCTAssertTrue(provider.receive(message: .context(nextSDKContext), from: NOPDatadogCore())) + core.messageBus.send(message: DatadogContext.mockRandom()) // receive initial SDK context + core.messageBus.send(message: rumAttributes) + core.messageBus.send(message: nextSDKContext) // Then provider.flush() @@ -244,8 +258,8 @@ class CrashContextProviderTests: XCTestCase { let logAttributes: LogEventAttributes = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(sdkContext), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(logAttributes), from: NOPDatadogCore())) + core.messageBus.send(message: sdkContext) // receive initial SDK context + core.messageBus.send(message: logAttributes) // Then provider.flush() @@ -264,9 +278,9 @@ class CrashContextProviderTests: XCTestCase { let nextSDKContext: DatadogContext = .mockRandom() // When - XCTAssertTrue(provider.receive(message: .context(.mockRandom()), from: NOPDatadogCore())) // receive initial SDK context - XCTAssertTrue(provider.receive(message: .payload(logAttributes), from: NOPDatadogCore())) - XCTAssertTrue(provider.receive(message: .context(nextSDKContext), from: NOPDatadogCore())) + core.messageBus.send(message: DatadogContext.mockRandom()) // receive initial SDK context + core.messageBus.send(message: logAttributes) + core.messageBus.send(message: nextSDKContext) // Then provider.flush() @@ -279,24 +293,27 @@ class CrashContextProviderTests: XCTestCase { // MARK: - Thread safety func testWhenContextIsWrittenAndReadFromDifferentThreads_itRunsAllOperationsSafely() { - let provider = CrashContextCoreProvider() + let localProvider = CrashContextCoreProvider() + let localCore = PassthroughCoreMock() + localProvider.subscribe(to: localCore.messageBus) + let viewEvent: RUMViewEvent = .mockRandom() let sessionState: RUMSessionState = .mockRandom() // swiftlint:disable opening_brace callConcurrently( closures: [ - { _ = provider.currentCrashContext }, - { _ = provider.receive(message: .context(.mockRandom()), from: NOPDatadogCore()) }, - { _ = provider.receive(message: .payload(viewEvent), from: NOPDatadogCore()) }, - { _ = provider.receive(message: .payload(RUMPayloadMessages.viewReset), from: NOPDatadogCore()) }, - { _ = provider.receive(message: .payload(sessionState), from: NOPDatadogCore()) }, + { _ = localProvider.currentCrashContext }, + { localCore.messageBus.send(message: DatadogContext.mockRandom()) }, + { localCore.messageBus.send(message: viewEvent) }, + { localCore.messageBus.send(message: RUMViewReset()) }, + { localCore.messageBus.send(message: sessionState) }, ], iterations: 50 ) // swiftlint:enable opening_brace - provider.flush() + localProvider.flush() } // MARK: - Helpers diff --git a/DatadogCore/Tests/Datadog/CrashReporting/CrashReporterTests.swift b/DatadogCore/Tests/Datadog/CrashReporting/CrashReporterTests.swift index f2c3f35531..98a654c9d5 100644 --- a/DatadogCore/Tests/Datadog/CrashReporting/CrashReporterTests.swift +++ b/DatadogCore/Tests/Datadog/CrashReporting/CrashReporterTests.swift @@ -57,7 +57,8 @@ class CrashReporterTests: XCTestCase { let crashReport: DDCrashReport = .mockRandomWith(context: crashContext) let rumCrashReceiver = CrashReceiverMock() - let core = PassthroughCoreMock(messageReceiver: rumCrashReceiver) + let core = PassthroughCoreMock() + core.subscribe(receiver: rumCrashReceiver) let plugin = CrashReportingPluginMock() diff --git a/DatadogCore/Tests/Datadog/DatadogCore/CoreMessageBusTests.swift b/DatadogCore/Tests/Datadog/DatadogCore/CoreMessageBusTests.swift new file mode 100644 index 0000000000..d4cc24aeff --- /dev/null +++ b/DatadogCore/Tests/Datadog/DatadogCore/CoreMessageBusTests.swift @@ -0,0 +1,266 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-Present Datadog, Inc. + */ + +import XCTest +import TestUtilities +import DatadogInternal + +@testable import DatadogCore + +class CoreMessageBusTests: XCTestCase { + func testCoreMessageBus() throws { + let expectation = XCTestExpectation(description: "dispatch message") + expectation.expectedFulfillmentCount = 2 + + // Given + let core = PassthroughCoreMock() + + let receiver = FeatureMessageReceiverMock { message in + // Then + switch message { + case let .payload(payload as String) where payload == "value": + expectation.fulfill() + default: + XCTFail("wrong message case") + } + } + + let bus = CoreMessageBus() + bus.connect(core: core) + + bus.connect(receiver, forKey: "receiver 1") + bus.connect(receiver, forKey: "receiver 2") + + // When + bus.send(message: .payload("value")) + + // Then + wait(for: [expectation], timeout: 0.5) + bus.flush() + } + + func testItForwardConfigurationAfterDispatch() throws { + let expectation = XCTestExpectation(description: "dispatch configuration") + let receiver = FeatureMessageReceiverMock { message in + guard + case .telemetry(let telemetry) = message, + case .configuration(let configuration) = telemetry + else { + return XCTFail("Message bus should send configuration telemetry") + } + + XCTAssertEqual(configuration.batchSize, 1) + XCTAssertTrue(configuration.trackErrors ?? false) + expectation.fulfill() + } + + // Given + let core = PassthroughCoreMock() + let bus = CoreMessageBus(configurationDispatchTime: .milliseconds(90)) + bus.connect(core: core) + bus.connect(receiver, forKey: "test") + + // When + bus.configuration(batchSize: 1) + bus.configuration(trackErrors: true) + + // Then + wait(for: [expectation], timeout: 0.5) + bus.flush() + } + + // MARK: - typed bus (subscribe / unsubscribe / send) + + func testSubscribe_deliversMessageToReceiver() throws { + let expectation = XCTestExpectation(description: "receiver invoked") + + // Given + let core = PassthroughCoreMock() + let bus = CoreMessageBus() + bus.connect(core: core) + + let receiver = AlphaReceiver { message, _ in + XCTAssertEqual(message.value, "value") + expectation.fulfill() + } + bus.subscribe(receiver: receiver) + + // When + bus.send(message: AlphaMessage(value: "value")) + + // Then + wait(for: [expectation], timeout: 0.5) + bus.flush() + } + + func testSubscribe_routesByMessageType() throws { + let expectation = XCTestExpectation(description: "matching receiver invoked") + expectation.assertForOverFulfill = true + + // Given + let core = PassthroughCoreMock() + let bus = CoreMessageBus() + bus.connect(core: core) + + let alpha = AlphaReceiver { _, _ in expectation.fulfill() } + let beta = BetaReceiver { _, _ in + XCTFail("BetaReceiver must not receive AlphaMessage") + } + bus.subscribe(receiver: alpha) + bus.subscribe(receiver: beta) + + // When + bus.send(message: AlphaMessage(value: "value")) + + // Then + wait(for: [expectation], timeout: 0.5) + bus.flush() + } + + func testSubscribe_deliversToMultipleReceiversOfSameType() throws { + let expectation = XCTestExpectation(description: "all receivers invoked") + expectation.expectedFulfillmentCount = 3 + + // Given + let core = PassthroughCoreMock() + let bus = CoreMessageBus() + bus.connect(core: core) + + let receivers = (0..<3).map { _ in + AlphaReceiver { _, _ in expectation.fulfill() } + } + receivers.forEach { bus.subscribe(receiver: $0) } + + // When + bus.send(message: AlphaMessage(value: "value")) + + // Then + wait(for: [expectation], timeout: 0.5) + bus.flush() + } + + func testUnsubscribe_stopsDelivery() throws { + let fallbackExpectation = XCTestExpectation(description: "fallback invoked") + + // Given + let core = PassthroughCoreMock() + let bus = CoreMessageBus() + bus.connect(core: core) + + let receiver = AlphaReceiver { _, _ in + XCTFail("receiver must not be invoked after unsubscribe") + } + bus.subscribe(receiver: receiver) + bus.unsubscribe(receiver: receiver) + + // When + bus.send(message: AlphaMessage(value: "value")) { fallbackExpectation.fulfill() } + + // Then + wait(for: [fallbackExpectation], timeout: 0.5) + bus.flush() + } + + func testSend_callsFallbackWhenNoSubscribers() throws { + let expectation = XCTestExpectation(description: "fallback invoked") + + // Given + let core = PassthroughCoreMock() + let bus = CoreMessageBus() + bus.connect(core: core) + + // When + bus.send(message: AlphaMessage(value: "value")) { expectation.fulfill() } + + // Then + wait(for: [expectation], timeout: 0.5) + bus.flush() + } + + func testSend_callsFallbackWhenCoreNotConnected() throws { + let expectation = XCTestExpectation(description: "fallback invoked") + + // Given (no `connect(core:)` call) + let bus = CoreMessageBus() + let receiver = AlphaReceiver { _, _ in + XCTFail("receiver must not be invoked without a connected core") + } + bus.subscribe(receiver: receiver) + + // When + bus.send(message: AlphaMessage(value: "value")) { expectation.fulfill() } + + // Then + wait(for: [expectation], timeout: 0.5) + bus.flush() + } + + func testSend_doesNotCallFallbackWhenDelivered() throws { + let delivery = XCTestExpectation(description: "receiver invoked") + let fallback = XCTestExpectation(description: "fallback NOT invoked") + fallback.isInverted = true + + // Given + let core = PassthroughCoreMock() + let bus = CoreMessageBus() + bus.connect(core: core) + + let receiver = AlphaReceiver { _, _ in delivery.fulfill() } + bus.subscribe(receiver: receiver) + + // When + bus.send(message: AlphaMessage(value: "value")) { fallback.fulfill() } + + // Then + wait(for: [delivery, fallback], timeout: 0.5) + bus.flush() + } +} + +// MARK: - typed-bus fixtures + +private struct AlphaMessage: BusMessage { + static let key = "test.alpha" + let value: String +} + +private struct BetaMessage: BusMessage { + static let key = "test.beta" +} + +private final class AlphaReceiver: BusMessageReceiver { + typealias Message = AlphaMessage + + let onReceive: (AlphaMessage, DatadogCoreProtocol) -> Void + + init(_ onReceive: @escaping (AlphaMessage, DatadogCoreProtocol) -> Void) { + self.onReceive = onReceive + } + + func receive(message: AlphaMessage, from core: DatadogCoreProtocol) { + onReceive(message, core) + } +} + +private final class BetaReceiver: BusMessageReceiver { + typealias Message = BetaMessage + + let onReceive: (BetaMessage, DatadogCoreProtocol) -> Void + + init(_ onReceive: @escaping (BetaMessage, DatadogCoreProtocol) -> Void) { + self.onReceive = onReceive + } + + func receive(message: BetaMessage, from core: DatadogCoreProtocol) { + onReceive(message, core) + } +} + +extension CoreMessageBus: @retroactive Telemetry { + public func send(telemetry: DatadogInternal.TelemetryMessage) { + send(message: .telemetry(telemetry)) + } +} diff --git a/DatadogCore/Tests/Datadog/Extensions/ContextSharing/ContextSharingTransformerTests.swift b/DatadogCore/Tests/Datadog/Extensions/ContextSharing/ContextSharingTransformerTests.swift index b5df47d5c6..82a14b0fcb 100644 --- a/DatadogCore/Tests/Datadog/Extensions/ContextSharing/ContextSharingTransformerTests.swift +++ b/DatadogCore/Tests/Datadog/Extensions/ContextSharing/ContextSharingTransformerTests.swift @@ -27,25 +27,11 @@ class ContextSharingTransformerTests: XCTestCase { func testReceiveContextMessage_transformsToSharedContext() throws { // Given let transformer = ContextSharingTransformer() - let message = FeatureMessage.context(.mockRandom()) // When - let handled = transformer.receive(message: message, from: core) + transformer.receive(message: .mockRandom(), from: core) - // Then - XCTAssertTrue(handled) - } - - func testReceiveNonContextMessage_returnsNotHandled() throws { - // Given - let transformer = ContextSharingTransformer() - let customMessage = FeatureMessage.payload("") - - // When - let handled = transformer.receive(message: customMessage, from: core) - - // Then - XCTAssertFalse(handled) + // Then — no assertion needed; just checking it doesn't crash } func testPublish_callsReceiverImmediately() throws { @@ -78,8 +64,7 @@ class ContextSharingTransformerTests: XCTestCase { let userInfo = UserInfo(id: "user-456") let accountInfo = AccountInfo(id: "account-789") let context = DatadogContext.mockWith(userInfo: userInfo, accountInfo: accountInfo) - let message = FeatureMessage.context(context) - _ = transformer.receive(message: message, from: core) + transformer.receive(message: context, from: core) // Then XCTAssertEqual(receivedContexts.count, 2) @@ -101,8 +86,7 @@ class ContextSharingTransformerTests: XCTestCase { transformer.cancel() let context = DatadogContext.mockWith(userInfo: UserInfo(id: "user-789")) - let message = FeatureMessage.context(context) - _ = transformer.receive(message: message, from: core) + transformer.receive(message: context, from: core) // Then - receiver must not be called after cancel XCTAssertEqual(callCount, 1) // Only initial call from publish diff --git a/DatadogCore/Tests/Datadog/FeaturesIntegration/TracingWithLoggingIntegrationTests.swift b/DatadogCore/Tests/Datadog/FeaturesIntegration/TracingWithLoggingIntegrationTests.swift index 86165eb71b..1b85f3f493 100644 --- a/DatadogCore/Tests/Datadog/FeaturesIntegration/TracingWithLoggingIntegrationTests.swift +++ b/DatadogCore/Tests/Datadog/FeaturesIntegration/TracingWithLoggingIntegrationTests.swift @@ -17,7 +17,8 @@ class TracingWithLoggingIntegrationTests: XCTestCase { override func setUp() { super.setUp() - core = PassthroughCoreMock(messageReceiver: LogMessageReceiver.mockAny()) + core = PassthroughCoreMock() + core.subscribe(receiver: LogMessageReceiver.mockAny()) } override func tearDown() { diff --git a/DatadogCore/Tests/Datadog/TracerTests.swift b/DatadogCore/Tests/Datadog/TracerTests.swift index 972a49d260..2672de9cc3 100644 --- a/DatadogCore/Tests/Datadog/TracerTests.swift +++ b/DatadogCore/Tests/Datadog/TracerTests.swift @@ -618,10 +618,9 @@ class TracerTests: XCTestCase { // MARK: - Integration With Logging Feature func testSendingSpanLogs() throws { - let logging: LogsFeature = .mockWith( - messageReceiver: LogMessageReceiver.mockAny() - ) + let logging: LogsFeature = .mockWith() try core.register(feature: logging) + core.messageBus.subscribe(receiver: logging.logMessageReceiver) Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) @@ -651,10 +650,9 @@ class TracerTests: XCTestCase { } func testSendingSpanLogsWithErrorFromArguments() throws { - let logging: LogsFeature = .mockWith( - messageReceiver: LogMessageReceiver.mockAny() - ) + let logging: LogsFeature = .mockWith() try core.register(feature: logging) + core.messageBus.subscribe(receiver: logging.logMessageReceiver) Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) @@ -676,10 +674,9 @@ class TracerTests: XCTestCase { } func testSendingSpanLogsWithErrorFromNSError() throws { - let logging: LogsFeature = .mockWith( - messageReceiver: LogMessageReceiver.mockAny() - ) + let logging: LogsFeature = .mockWith() try core.register(feature: logging) + core.messageBus.subscribe(receiver: logging.logMessageReceiver) Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) @@ -707,10 +704,9 @@ class TracerTests: XCTestCase { } func testSendingSpanLogsWithErrorFromSwiftError() throws { - let logging: LogsFeature = .mockWith( - messageReceiver: LogMessageReceiver.mockAny() - ) + let logging: LogsFeature = .mockWith() try core.register(feature: logging) + core.messageBus.subscribe(receiver: logging.logMessageReceiver) Trace.enable(with: config, in: core) let tracer = Tracer.shared(in: core) diff --git a/DatadogCore/Tests/Datadog/Tracing/TracingURLSessionHandlerTests.swift b/DatadogCore/Tests/Datadog/Tracing/TracingURLSessionHandlerTests.swift index 90c76cee88..49dbb243bf 100644 --- a/DatadogCore/Tests/Datadog/Tracing/TracingURLSessionHandlerTests.swift +++ b/DatadogCore/Tests/Datadog/Tracing/TracingURLSessionHandlerTests.swift @@ -21,10 +21,9 @@ class TracingURLSessionHandlerTests: XCTestCase { override func setUp() { super.setUp() let receiver = ContextMessageReceiver(samplerProvider: SamplerProvider(sampleRate: .mockAny())) - core = PassthroughCoreMock(messageReceiver: CombinedFeatureMessageReceiver([ - LogMessageReceiver.mockAny(), - receiver - ])) + core = PassthroughCoreMock() + core.subscribe(receiver: receiver) + core.subscribe(receiver: LogMessageReceiver.mockAny()) tracer = .mockWith( core: core, @@ -225,8 +224,7 @@ class TracingURLSessionHandlerTests: XCTestCase { ) ] ) - let message = FeatureMessage.context(fakeContext) - _ = handler.contextReceiver.receive(message: message, from: core) + handler.contextReceiver.receive(message: fakeContext, from: core) let (modifiedRequest, _, _) = handler.modify( request: request, headerTypes: [.datadog, .tracecontext, .b3, .b3multi], diff --git a/DatadogCrashReporting/Sources/CrashContextProvider.swift b/DatadogCrashReporting/Sources/CrashContextProvider.swift index 6216d5f116..3cd38cff4e 100644 --- a/DatadogCrashReporting/Sources/CrashContextProvider.swift +++ b/DatadogCrashReporting/Sources/CrashContextProvider.swift @@ -47,6 +47,9 @@ internal class CrashContextCoreProvider: CrashContextProvider { didSet { _context?.lastRUMAttributes = rumAttributes } } + /// Typed-bus subscription handles. Retaining these keeps the closures alive. + private var subscriptions: [MessageBusSubscription] = [] + // MARK: - CrashContextProviderType var currentCrashContext: CrashContext? { @@ -61,30 +64,13 @@ internal class CrashContextCoreProvider: CrashContextProvider { extension CrashContextCoreProvider: FeatureMessageReceiver { func receive(message: FeatureMessage, from core: DatadogCoreProtocol) -> Bool { - switch message { - case .context(let context): - update(context: context) - case let .payload(viewEvent as RUMViewEvent): - queue.async { self.viewEvent = viewEvent } - case let .payload(message as String) where message == RUMPayloadMessages.viewReset: - queue.async { self.viewEvent = nil } - case let .payload(sessionState as RUMSessionState): - queue.async { self.sessionState = sessionState } - case let .payload(rumAttributes as RUMEventAttributes): - queue.async { self.rumAttributes = rumAttributes } - case let .payload(logAttributes as LogEventAttributes): - queue.async { self.logAttributes = logAttributes } - default: - return false - } - - return true + return false } /// Updates crash context. /// /// - Parameter context: The updated core context. - private func update(context: DatadogContext) { + fileprivate func update(context: DatadogContext) { queue.async { [weak self] in guard let self = self else { return @@ -105,6 +91,37 @@ extension CrashContextCoreProvider: FeatureMessageReceiver { } } +// MARK: - Typed-bus subscriptions + +extension CrashContextCoreProvider { + /// Subscribes to all crash-context update messages on the typed bus. + /// + /// Call once from `CrashReporting.enableOrThrow`. The returned subscriptions are retained + /// by the provider and cancelled when it is deallocated. + func subscribe(to bus: MessageBus) { + subscriptions = [ + bus.subscribe { [weak self] (context: DatadogContext, _) in + self?.update(context: context) + }, + bus.subscribe { [weak self] (message: RUMViewEvent, _) in + self?.queue.async { self?.viewEvent = message } + }, + bus.subscribe { [weak self] (_: RUMViewReset, _) in + self?.queue.async { self?.viewEvent = nil } + }, + bus.subscribe { [weak self] (message: RUMSessionState, _) in + self?.queue.async { self?.sessionState = message } + }, + bus.subscribe { [weak self] (message: RUMEventAttributes, _) in + self?.queue.async { self?.rumAttributes = message } + }, + bus.subscribe { [weak self] (message: LogEventAttributes, _) in + self?.queue.async { self?.logAttributes = message } + }, + ] + } +} + extension CrashContextCoreProvider: Flushable { /// Awaits completion of all asynchronous operations. /// diff --git a/DatadogCrashReporting/Sources/CrashReportSender.swift b/DatadogCrashReporting/Sources/CrashReportSender.swift index 0103d43ca4..bced563979 100644 --- a/DatadogCrashReporting/Sources/CrashReportSender.swift +++ b/DatadogCrashReporting/Sources/CrashReportSender.swift @@ -41,10 +41,8 @@ internal struct MessageBusSender: CrashReportSender { return } - core.send( - message: .payload( - Crash(report: report, context: context) - ), + core.messageBus.send( + message: Crash(report: report, context: context), else: { DD.logger.warn( """ diff --git a/DatadogCrashReporting/Sources/CrashReporting.swift b/DatadogCrashReporting/Sources/CrashReporting.swift index e00c74523d..7ec5e0a7f0 100644 --- a/DatadogCrashReporting/Sources/CrashReporting.swift +++ b/DatadogCrashReporting/Sources/CrashReporting.swift @@ -62,6 +62,9 @@ public final class CrashReporting { try core.register(feature: reporter) + // Subscribe typed-bus receivers for crash context updates: + contextProvider.subscribe(to: core.messageBus) + if let backtraceReporter = plugin.backtraceReporter { try core.register(backtraceReporter: backtraceReporter) } diff --git a/DatadogCrashReporting/Tests/CrashContextCoreProviderTests.swift b/DatadogCrashReporting/Tests/CrashContextCoreProviderTests.swift index bb973fa8bb..3d5c0f03ce 100644 --- a/DatadogCrashReporting/Tests/CrashContextCoreProviderTests.swift +++ b/DatadogCrashReporting/Tests/CrashContextCoreProviderTests.swift @@ -11,25 +11,36 @@ import DatadogInternal @testable import DatadogCrashReporting class CrashContextCoreProviderTests: XCTestCase { + // swiftlint:disable implicitly_unwrapped_optional + private var core: PassthroughCoreMock! + private var provider: CrashContextCoreProvider! + // swiftlint:enable implicitly_unwrapped_optional + + override func setUp() { + super.setUp() + core = PassthroughCoreMock() + provider = CrashContextCoreProvider() + provider.subscribe(to: core.messageBus) + } + + override func tearDown() { + core = nil + provider = nil + super.tearDown() + } + // MARK: - Context Update Tests func testItUpdatesContextFromDatadogContext() { - // Given - let provider = CrashContextCoreProvider() - let core = PassthroughCoreMock() - let context: DatadogContext = .mockWith( + // When + core.messageBus.send(message: DatadogContext.mockWith( service: "test-service", env: "test-env", version: "1.0.0" - ) - - // When - let message: FeatureMessage = .context(context) - XCTAssertTrue(provider.receive(message: message, from: core)) + )) provider.flush() // Then - XCTAssertNotNil(provider.currentCrashContext) XCTAssertEqual(provider.currentCrashContext?.service, "test-service") XCTAssertEqual(provider.currentCrashContext?.env, "test-env") XCTAssertEqual(provider.currentCrashContext?.version, "1.0.0") @@ -37,21 +48,16 @@ class CrashContextCoreProviderTests: XCTestCase { func testItDoesNotUpdateContextWhenContextIsUnchanged() { // Given - let provider = CrashContextCoreProvider() - let core = PassthroughCoreMock() let context: DatadogContext = .mockWith(service: "test-service") var callbackCount = 0 - - provider.onCrashContextChange = { _ in - callbackCount += 1 - } + provider.onCrashContextChange = { _ in callbackCount += 1 } // When - XCTAssertTrue(provider.receive(message: .context(context), from: core)) - XCTAssertTrue(provider.receive(message: .context(context), from: core)) + core.messageBus.send(message: context) + core.messageBus.send(message: context) provider.flush() - // Then - callback should only be called once for the actual change + // Then — callback fires once per distinct value XCTAssertEqual(callbackCount, 1) } @@ -59,36 +65,27 @@ class CrashContextCoreProviderTests: XCTestCase { func testItStoresRUMViewEvent() { // Given - let provider = CrashContextCoreProvider() - let core = PassthroughCoreMock() - let context: DatadogContext = .mockAny() let viewEvent: RUMViewEvent = .mockRandom() + core.messageBus.send(message: DatadogContext.mockAny()) // When - XCTAssertTrue(provider.receive(message: .context(context), from: core)) - XCTAssertTrue(provider.receive(message: .payload(viewEvent), from: core)) + core.messageBus.send(message: viewEvent) provider.flush() // Then - XCTAssertNotNil(provider.currentCrashContext?.lastRUMViewEvent) XCTAssertEqual(provider.currentCrashContext?.lastRUMViewEvent?.view.id, viewEvent.view.id) } func testItResetsRUMViewEventOnViewReset() { // Given - let provider = CrashContextCoreProvider() - let core = PassthroughCoreMock() - let context: DatadogContext = .mockAny() let viewEvent: RUMViewEvent = .mockRandom() - - XCTAssertTrue(provider.receive(message: .context(context), from: core)) - XCTAssertTrue(provider.receive(message: .payload(viewEvent), from: core)) + core.messageBus.send(message: DatadogContext.mockAny()) + core.messageBus.send(message: viewEvent) provider.flush() - XCTAssertNotNil(provider.currentCrashContext?.lastRUMViewEvent) // When - XCTAssertTrue(provider.receive(message: .payload(RUMPayloadMessages.viewReset), from: core)) + core.messageBus.send(message: RUMViewReset()) provider.flush() // Then @@ -99,18 +96,15 @@ class CrashContextCoreProviderTests: XCTestCase { func testItStoresRUMSessionState() { // Given - let provider = CrashContextCoreProvider() - let core = PassthroughCoreMock() - let context: DatadogContext = .mockAny() let sessionState: RUMSessionState = .mockRandom() + core.messageBus.send(message: DatadogContext.mockAny()) + provider.flush() // When - XCTAssertTrue(provider.receive(message: .context(context), from: core)) - XCTAssertTrue(provider.receive(message: .payload(sessionState), from: core)) + core.messageBus.send(message: sessionState) provider.flush() // Then - XCTAssertNotNil(provider.currentCrashContext?.lastRUMSessionState) XCTAssertEqual(provider.currentCrashContext?.lastRUMSessionState?.sessionUUID, sessionState.sessionUUID) } @@ -118,18 +112,12 @@ class CrashContextCoreProviderTests: XCTestCase { func testItInvokesCallbackOnContextChange() { // Given - let provider = CrashContextCoreProvider() - let core = PassthroughCoreMock() - let context: DatadogContext = .mockWith(service: "test-service") var receivedContext: CrashContext? - - provider.onCrashContextChange = { crashContext in - receivedContext = crashContext - } + provider.onCrashContextChange = { receivedContext = $0 } provider.flush() // When - XCTAssertTrue(provider.receive(message: .context(context), from: core)) + core.messageBus.send(message: DatadogContext.mockWith(service: "test-service")) provider.flush() // Then @@ -137,18 +125,14 @@ class CrashContextCoreProviderTests: XCTestCase { XCTAssertEqual(receivedContext?.service, "test-service") } - // MARK: - Message Handling Tests + // MARK: - Initial State Tests - func testItReturnsFalseForUnhandledMessages() { + func testCrashContextIsNilUntilFirstContextMessageIsReceived() { // Given let provider = CrashContextCoreProvider() - let core = PassthroughCoreMock() - let unrelatedMessage = "some unrelated message" + provider.subscribe(to: core.messageBus) - // When - let handled = provider.receive(message: .payload(unrelatedMessage), from: core) - - // Then - XCTAssertFalse(handled) + // Then — no message sent yet + XCTAssertNil(provider.currentCrashContext) } } diff --git a/DatadogCrashReporting/Tests/CrashReportSenderTests.swift b/DatadogCrashReporting/Tests/CrashReportSenderTests.swift index c84ba3c389..64b0e623cb 100644 --- a/DatadogCrashReporting/Tests/CrashReportSenderTests.swift +++ b/DatadogCrashReporting/Tests/CrashReportSenderTests.swift @@ -16,7 +16,8 @@ class CrashReportSenderTests: XCTestCase { func testItSendsCrashReportWhenTrackingConsentIsGranted() { // Given let receiver = CrashReceiverMock() - let core = PassthroughCoreMock(messageReceiver: receiver) + let core = PassthroughCoreMock() + core.subscribe(receiver: receiver) let sender = MessageBusSender(core: core) let crashContext: CrashContext = .mockWith(trackingConsent: .granted) let crashReport: DDCrashReport = .mockAny() @@ -33,7 +34,8 @@ class CrashReportSenderTests: XCTestCase { func testItDoesNotSendCrashReportWhenTrackingConsentIsNotGranted() { // Given let receiver = CrashReceiverMock() - let core = PassthroughCoreMock(messageReceiver: receiver) + let core = PassthroughCoreMock() + core.subscribe(receiver: receiver) let sender = MessageBusSender(core: core) let crashContext: CrashContext = .mockWith(trackingConsent: .notGranted) let crashReport: DDCrashReport = .mockAny() @@ -48,7 +50,8 @@ class CrashReportSenderTests: XCTestCase { func testItDoesNotSendCrashReportWhenTrackingConsentIsPending() { // Given let receiver = CrashReceiverMock() - let core = PassthroughCoreMock(messageReceiver: receiver) + let core = PassthroughCoreMock() + core.subscribe(receiver: receiver) let sender = MessageBusSender(core: core) let crashContext: CrashContext = .mockWith(trackingConsent: .pending) let crashReport: DDCrashReport = .mockAny() @@ -63,7 +66,8 @@ class CrashReportSenderTests: XCTestCase { func testItSendsCrashReportWithCorrectContext() { // Given let receiver = CrashReceiverMock() - let core = PassthroughCoreMock(messageReceiver: receiver) + let core = PassthroughCoreMock() + core.subscribe(receiver: receiver) let sender = MessageBusSender(core: core) let crashContext: CrashContext = .mockWith( service: "test-service", diff --git a/DatadogFlags/Sources/Client/FlagsClient.swift b/DatadogFlags/Sources/Client/FlagsClient.swift index cc2f837cd9..5768f19984 100644 --- a/DatadogFlags/Sources/Client/FlagsClient.swift +++ b/DatadogFlags/Sources/Client/FlagsClient.swift @@ -189,7 +189,7 @@ public final class FlagsClient { ), exposureLogger: feature.makeExposureLogger(featureScope), evaluationLogger: feature.makeEvaluationLogger(featureScope), - rumFlagEvaluationReporter: feature.makeRUMFlagEvaluationReporter(featureScope) + rumFlagEvaluationReporter: feature.makeRUMFlagEvaluationReporter() ) feature.clientRegistry.register(client, named: name) diff --git a/DatadogFlags/Sources/Client/RUMFlagEvaluationReporter.swift b/DatadogFlags/Sources/Client/RUMFlagEvaluationReporter.swift index 9934046a5f..c5e2255bb3 100644 --- a/DatadogFlags/Sources/Client/RUMFlagEvaluationReporter.swift +++ b/DatadogFlags/Sources/Client/RUMFlagEvaluationReporter.swift @@ -12,19 +12,17 @@ internal protocol RUMFlagEvaluationReporting { } internal final class RUMFlagEvaluationReporter: RUMFlagEvaluationReporting { - private let featureScope: any FeatureScope + private let messageBus: any MessageBus - init(featureScope: any FeatureScope) { - self.featureScope = featureScope + init(messageBus: any MessageBus) { + self.messageBus = messageBus } func sendFlagEvaluation(flagKey: String, value: T) where T: FlagValue { - featureScope.send( - message: .payload( - RUMFlagEvaluationMessage( - flagKey: flagKey, - value: value - ) + messageBus.send( + message: RUMFlagEvaluationMessage( + flagKey: flagKey, + value: value ) ) } diff --git a/DatadogFlags/Sources/Feature/FlagsFeature.swift b/DatadogFlags/Sources/Feature/FlagsFeature.swift index 4be5725a08..3da108818d 100644 --- a/DatadogFlags/Sources/Feature/FlagsFeature.swift +++ b/DatadogFlags/Sources/Feature/FlagsFeature.swift @@ -21,7 +21,7 @@ internal struct FlagsFeature: DatadogRemoteFeature { let clientRegistry: FlagsClientRegistry let makeExposureLogger: (any FeatureScope) -> any ExposureLogging let makeEvaluationLogger: (any FeatureScope) -> any EvaluationLogging - let makeRUMFlagEvaluationReporter: (any FeatureScope) -> any RUMFlagEvaluationReporting + let makeRUMFlagEvaluationReporter: () -> any RUMFlagEvaluationReporting let performanceOverride: PerformancePresetOverride? let issueReporter: IssueReporter private let evaluationAggregator: EvaluationAggregator? @@ -76,11 +76,11 @@ internal struct FlagsFeature: DatadogRemoteFeature { return EvaluationLogger(aggregator: aggregator) } - makeRUMFlagEvaluationReporter = { featureScope in + makeRUMFlagEvaluationReporter = { [messageBus = core.messageBus] in guard configuration.rumIntegrationEnabled else { return NOPRUMFlagEvaluationReporter() } - return RUMFlagEvaluationReporter(featureScope: featureScope) + return RUMFlagEvaluationReporter(messageBus: messageBus) } performanceOverride = PerformancePresetOverride(maxObjectsInFile: 50) diff --git a/DatadogFlags/Tests/Client/FlagsClientTests.swift b/DatadogFlags/Tests/Client/FlagsClientTests.swift index 4f33d9314f..b746c9a016 100644 --- a/DatadogFlags/Tests/Client/FlagsClientTests.swift +++ b/DatadogFlags/Tests/Client/FlagsClientTests.swift @@ -308,15 +308,18 @@ final class FlagsClientTests: XCTestCase { date: .mockAny() ) let data = try JSONEncoder().encode(initialState) - let messageReceiver = FeatureMessageReceiverMock() let core = SingleFeatureCoreMock( dataStore: DataStoreMock( storage: [ FlagsClient.defaultName: .value(data, dataStoreDefaultKeyVersion) ] - ), - messageReceiver: messageReceiver + ) ) + var rumMessages: [RUMFlagEvaluationMessage] = [] + let subscription = core.messageBus.subscribe { (msg: RUMFlagEvaluationMessage, _) in + rumMessages.append(msg) + } + defer { core.messageBus.unsubscribe(subscription) } // When Flags.enable(with: .init(trackExposures: false), in: core) @@ -326,7 +329,7 @@ final class FlagsClientTests: XCTestCase { // Then XCTAssertEqual(core.events(ofType: ExposureEvent.self).count, 0, "No exposure events should be written") - XCTAssertEqual(messageReceiver.messages.filter(\.isRUMMessage).count, 1, "RUM integration should still work") + XCTAssertEqual(rumMessages.count, 1, "RUM integration should still work") } func testRUMIntegrationDisabled() throws { @@ -337,15 +340,18 @@ final class FlagsClientTests: XCTestCase { date: .mockAny() ) let data = try JSONEncoder().encode(initialState) - let messageReceiver = FeatureMessageReceiverMock() let core = SingleFeatureCoreMock( dataStore: DataStoreMock( storage: [ FlagsClient.defaultName: .value(data, dataStoreDefaultKeyVersion) ] - ), - messageReceiver: messageReceiver + ) ) + var rumMessages: [RUMFlagEvaluationMessage] = [] + let subscription = core.messageBus.subscribe { (msg: RUMFlagEvaluationMessage, _) in + rumMessages.append(msg) + } + defer { core.messageBus.unsubscribe(subscription) } // When Flags.enable(with: .init(rumIntegrationEnabled: false), in: core) @@ -355,7 +361,7 @@ final class FlagsClientTests: XCTestCase { // Then XCTAssertEqual(core.events(ofType: ExposureEvent.self).count, 1, "Exposure should still be logged") - XCTAssertEqual(messageReceiver.messages.filter(\.isRUMMessage).count, 0, "No RUM messages should be sent") + XCTAssertEqual(rumMessages.count, 0, "No RUM messages should be sent") } // MARK: - Internal methods consumed by the React Native SDK diff --git a/DatadogFlags/Tests/Client/RUMFlagEvaluationReporterTests.swift b/DatadogFlags/Tests/Client/RUMFlagEvaluationReporterTests.swift index c28c9fa895..2508fe25e2 100644 --- a/DatadogFlags/Tests/Client/RUMFlagEvaluationReporterTests.swift +++ b/DatadogFlags/Tests/Client/RUMFlagEvaluationReporterTests.swift @@ -10,12 +10,20 @@ import DatadogInternal @testable import DatadogFlags -final class RUMFlagEvaluationReporterTests: XCTestCase { - private let featureScope = FeatureScopeMock() +private final class RUMFlagEvaluationRecorder: BusMessageReceiver { + private(set) var messages: [RUMFlagEvaluationMessage] = [] + func receive(message: RUMFlagEvaluationMessage, from core: DatadogCoreProtocol) { + messages.append(message) + } +} +final class RUMFlagEvaluationReporterTests: XCTestCase { func testSendFlagEvaluation() throws { // Given - let reporter = RUMFlagEvaluationReporter(featureScope: featureScope) + let core = PassthroughCoreMock() + let recorder = RUMFlagEvaluationRecorder() + core.subscribe(receiver: recorder) + let reporter = RUMFlagEvaluationReporter(messageBus: core.messageBus) // When reporter.sendFlagEvaluation( @@ -24,10 +32,9 @@ final class RUMFlagEvaluationReporterTests: XCTestCase { ) // Then - let messages = featureScope.messagesSent() - XCTAssertEqual(messages.count, 1, "Should send flag evaluation message") + XCTAssertEqual(recorder.messages.count, 1, "Should send flag evaluation message") - let flagEvaluation = try XCTUnwrap(messages.firstPayload as? RUMFlagEvaluationMessage) + let flagEvaluation = try XCTUnwrap(recorder.messages.first) XCTAssertEqual(flagEvaluation.flagKey, "feature-flag") XCTAssertEqual(flagEvaluation.value as? Bool, true) } diff --git a/DatadogInternal/Sources/NetworkInstrumentation/DatadogURLSessionHandler.swift b/DatadogInternal/Sources/NetworkInstrumentation/DatadogURLSessionHandler.swift index 5adc207788..41c566cd69 100644 --- a/DatadogInternal/Sources/NetworkInstrumentation/DatadogURLSessionHandler.swift +++ b/DatadogInternal/Sources/NetworkInstrumentation/DatadogURLSessionHandler.swift @@ -46,8 +46,13 @@ extension DatadogCoreProtocol { /// /// - Parameter urlSessionHandler: The `URLSession` handler to register. public func register(urlSessionHandler: DatadogURLSessionHandler) throws { - let contextProvider = NetworkContextCoreProvider() - let feature = get(feature: NetworkInstrumentationFeature.self) ?? .init(networkContextProvider: contextProvider, messageReceiver: contextProvider) + let feature = get(feature: NetworkInstrumentationFeature.self) ?? { + let contextProvider = NetworkContextCoreProvider() + let feature = NetworkInstrumentationFeature(networkContextProvider: contextProvider, messageReceiver: NOPFeatureMessageReceiver()) + // Subscribe typed-bus receiver before registration so initial context push is received: + messageBus.subscribe(receiver: contextProvider) + return feature + }() feature.handlers.append(urlSessionHandler) try register(feature: feature) } diff --git a/DatadogInternal/Sources/NetworkInstrumentation/NetworkContextProvider.swift b/DatadogInternal/Sources/NetworkInstrumentation/NetworkContextProvider.swift index 86d658e437..e178d2b602 100644 --- a/DatadogInternal/Sources/NetworkInstrumentation/NetworkContextProvider.swift +++ b/DatadogInternal/Sources/NetworkInstrumentation/NetworkContextProvider.swift @@ -19,12 +19,8 @@ internal class NetworkContextCoreProvider: NetworkContextProvider { var currentNetworkContext: NetworkContext? } -extension NetworkContextCoreProvider: FeatureMessageReceiver { - func receive(message: FeatureMessage, from core: DatadogCoreProtocol) -> Bool { - guard case let .context(context) = message else { - return false - } - +extension NetworkContextCoreProvider: BusMessageReceiver { + func receive(message context: DatadogContext, from core: DatadogCoreProtocol) { let userConfigurationContext: UserConfigurationContext? = { guard let userInfo = context.userInfo else { return nil @@ -45,6 +41,5 @@ extension NetworkContextCoreProvider: FeatureMessageReceiver { userConfigurationContext: userConfigurationContext, accountConfigurationContext: accountConfigurationContext ) - return true } } diff --git a/DatadogInternal/Tests/NetworkInstrumentation/NetworkInstrumentationFeatureTests.swift b/DatadogInternal/Tests/NetworkInstrumentation/NetworkInstrumentationFeatureTests.swift index 734a1bffd9..c150ce9f81 100644 --- a/DatadogInternal/Tests/NetworkInstrumentation/NetworkInstrumentationFeatureTests.swift +++ b/DatadogInternal/Tests/NetworkInstrumentation/NetworkInstrumentationFeatureTests.swift @@ -2190,10 +2190,9 @@ class NetworkInstrumentationFeatureTests: XCTestCase { ) // When - let result = provider.receive(message: .context(context), from: core) + provider.receive(message: context, from: core) // Then - XCTAssertTrue(result) let networkContext = try XCTUnwrap(provider.currentNetworkContext) // Verify RUM context @@ -2222,10 +2221,9 @@ class NetworkInstrumentationFeatureTests: XCTestCase { ) // When - let result = provider.receive(message: .context(context), from: core) + provider.receive(message: context, from: core) // Then - XCTAssertTrue(result) let networkContext = try XCTUnwrap(provider.currentNetworkContext) // Verify RUM context is still available @@ -2237,15 +2235,11 @@ class NetworkInstrumentationFeatureTests: XCTestCase { XCTAssertNil(networkContext.accountConfigurationContext) } - func testWhenReceivingNonContextMessage_itReturnsFalse() { + func testNetworkContextIsNilUntilContextMessageIsReceived() { // Given let provider = NetworkContextCoreProvider() - // When - let result = provider.receive(message: .payload("some data"), from: core) - - // Then - XCTAssertFalse(result) + // Then — no DatadogContext delivered yet XCTAssertNil(provider.currentNetworkContext) } diff --git a/DatadogInternal/Tests/Telemetry/TelemetryTests.swift b/DatadogInternal/Tests/Telemetry/TelemetryTests.swift index 6515d3c7b7..baa8aa9e18 100644 --- a/DatadogInternal/Tests/Telemetry/TelemetryTests.swift +++ b/DatadogInternal/Tests/Telemetry/TelemetryTests.swift @@ -187,23 +187,24 @@ class TelemetryTests: XCTestCase { // MARK: - Integration with Core func testWhenUsingCoreTelemetry_itSendsTelemetryToMessageReceiver() throws { - let receiver = FeatureMessageReceiverMock() - let core = PassthroughCoreMock(messageReceiver: receiver) + let receiver = TelemetryReceiverMock() + let core = PassthroughCoreMock() + core.subscribe(receiver: receiver) core.telemetry.debug("debug message") - XCTAssertEqual(receiver.messages.lastTelemetry?.asDebug?.message, "debug message") + XCTAssertEqual(receiver.messages.firstDebug()?.message, "debug message") core.telemetry.error("error message") - XCTAssertEqual(receiver.messages.lastTelemetry?.asError?.message, "error message") + XCTAssertEqual(receiver.messages.firstError()?.message, "error message") core.telemetry.configuration(batchSize: 123) - XCTAssertEqual(receiver.messages.lastTelemetry?.asConfiguration?.batchSize, 123) + XCTAssertEqual(receiver.messages.firstConfiguration()?.batchSize, 123) core.telemetry.metric(name: "metric name", attributes: [:], sampleRate: 15) - XCTAssertEqual(receiver.messages.lastTelemetry?.asMetric?.name, "metric name") + XCTAssertEqual(receiver.messages.firstMetric(named: "metric name")?.name, "metric name") let metricTrace = core.telemetry.startMethodCalled(operationName: .mockAny(), callerClass: .mockAny(), headSampleRate: 100) core.telemetry.stopMethodCalled(metricTrace) - XCTAssertEqual(receiver.messages.lastTelemetry?.asMetric?.name, MethodCalledMetric.name) + XCTAssertEqual(receiver.messages.lastMetric(named: MethodCalledMetric.name)?.name, MethodCalledMetric.name) } } diff --git a/DatadogLogs/Sources/Feature/LogsFeature.swift b/DatadogLogs/Sources/Feature/LogsFeature.swift index d8b3ab7460..9cecce4eee 100644 --- a/DatadogLogs/Sources/Feature/LogsFeature.swift +++ b/DatadogLogs/Sources/Feature/LogsFeature.swift @@ -14,6 +14,12 @@ internal struct LogsFeature: DatadogRemoteFeature { let messageReceiver: FeatureMessageReceiver + /// Typed-bus receiver for `LogMessage`. + let logMessageReceiver: LogMessageReceiver + + /// Typed-bus receiver for WebView log events. + let webViewLogReceiver: WebViewLogReceiver + let logEventMapper: LogEventMapper? let backtraceReporter: BacktraceReporting? @@ -40,10 +46,7 @@ internal struct LogsFeature: DatadogRemoteFeature { customIntakeURL: customIntakeURL, telemetry: telemetry ), - messageReceiver: CombinedFeatureMessageReceiver( - LogMessageReceiver(logEventMapper: logEventMapper), - WebViewLogReceiver() - ), + messageReceiver: NOPFeatureMessageReceiver(), dateProvider: dateProvider, backtraceReporter: backtraceReporter ) @@ -59,6 +62,8 @@ internal struct LogsFeature: DatadogRemoteFeature { self.logEventMapper = logEventMapper self.requestBuilder = requestBuilder self.messageReceiver = messageReceiver + self.logMessageReceiver = LogMessageReceiver(logEventMapper: logEventMapper) + self.webViewLogReceiver = WebViewLogReceiver() self.dateProvider = dateProvider self.backtraceReporter = backtraceReporter self.attributes = SynchronizedAttributes(attributes: [:]) diff --git a/DatadogLogs/Sources/Feature/MessageReceivers.swift b/DatadogLogs/Sources/Feature/MessageReceivers.swift index c9c2220d73..18970c7bec 100644 --- a/DatadogLogs/Sources/Feature/MessageReceivers.swift +++ b/DatadogLogs/Sources/Feature/MessageReceivers.swift @@ -8,26 +8,21 @@ import Foundation import DatadogInternal /// Receiver to consume a Log message -internal struct LogMessageReceiver: FeatureMessageReceiver { +internal final class LogMessageReceiver: BusMessageReceiver { /// The log event mapper let logEventMapper: LogEventMapper? - /// Process messages receives from the bus. - /// - /// - Parameters: - /// - message: The Feature message - /// - core: The core from which the message is transmitted. - func receive(message: FeatureMessage, from core: DatadogCoreProtocol) -> Bool { - guard case let .payload(log as LogMessage) = message else { - return false - } + init(logEventMapper: LogEventMapper?) { + self.logEventMapper = logEventMapper + } + func receive(message log: LogMessage, from core: DatadogCoreProtocol) { core.scope(for: LogsFeature.self).eventWriteContext { context, writer in let builder = LogEventBuilder( service: log.service ?? context.service, loggerName: log.logger, networkInfoEnabled: log.networkInfoEnabled ?? false, - eventMapper: logEventMapper + eventMapper: self.logEventMapper ) builder.createLogEvent( @@ -56,22 +51,13 @@ internal struct LogMessageReceiver: FeatureMessageReceiver { callback: writer.write ) } - - return true } } /// Receiver to consume a Log event coming from Browser SDK. -internal struct WebViewLogReceiver: FeatureMessageReceiver { - /// Process messages receives from the bus. - /// - /// - Parameters: - /// - message: The Feature message - /// - core: The core from which the message is transmitted. - func receive(message: FeatureMessage, from core: DatadogCoreProtocol) -> Bool { - guard case var .webview(.log(event)) = message else { - return false - } +internal final class WebViewLogReceiver: BusMessageReceiver { + func receive(message: WebViewLogMessage, from core: DatadogCoreProtocol) { + var event = message.event let tagsKey = LogEventEncoder.StaticCodingKeys.tags.rawValue let dateKey = LogEventEncoder.StaticCodingKeys.date.rawValue @@ -105,7 +91,5 @@ internal struct WebViewLogReceiver: FeatureMessageReceiver { writer.write(value: AnyEncodable(event)) } - - return true } } diff --git a/DatadogLogs/Sources/Logger.swift b/DatadogLogs/Sources/Logger.swift index c50da3dc01..a8fb6fedfc 100644 --- a/DatadogLogs/Sources/Logger.swift +++ b/DatadogLogs/Sources/Logger.swift @@ -155,6 +155,7 @@ public struct Logger { return RemoteLogger( featureScope: core.scope(for: LogsFeature.self), + messageBus: core.messageBus, globalAttributes: feature.attributes, configuration: RemoteLogger.Configuration( service: configuration.service, diff --git a/DatadogLogs/Sources/Logs.swift b/DatadogLogs/Sources/Logs.swift index 2586c31c7b..b71ec94e0d 100644 --- a/DatadogLogs/Sources/Logs.swift +++ b/DatadogLogs/Sources/Logs.swift @@ -89,6 +89,10 @@ public enum Logs { ) try core.register(feature: feature) + + // Subscribe typed-bus receivers: + core.messageBus.subscribe(receiver: feature.logMessageReceiver) + core.messageBus.subscribe(receiver: feature.webViewLogReceiver) } /// Adds a custom attribute to all future logs sent by any logger created from the provided Core. @@ -120,10 +124,10 @@ public enum Logs { } private static func sendAttributesChanged(for feature: LogsFeature, in core: DatadogCoreProtocol) { - core.send( - message: .payload(LogEventAttributes( + core.messageBus.send( + message: LogEventAttributes( attributes: feature.attributes.getAttributes() - )) + ) ) } } diff --git a/DatadogLogs/Sources/RemoteLogger.swift b/DatadogLogs/Sources/RemoteLogger.swift index 970c548617..b17b66f28f 100644 --- a/DatadogLogs/Sources/RemoteLogger.swift +++ b/DatadogLogs/Sources/RemoteLogger.swift @@ -27,6 +27,8 @@ internal final class RemoteLogger: LoggerProtocol, Sendable { /// Logs feature scope. let featureScope: FeatureScope + /// Typed message bus, used to forward log errors to RUM. + let messageBus: MessageBus /// Configuration specific to this logger. let configuration: Configuration /// Date provider for logs. @@ -48,6 +50,7 @@ internal final class RemoteLogger: LoggerProtocol, Sendable { init( featureScope: FeatureScope, + messageBus: MessageBus, globalAttributes: SynchronizedAttributes, configuration: Configuration, dateProvider: DateProvider, @@ -56,6 +59,7 @@ internal final class RemoteLogger: LoggerProtocol, Sendable { backtraceReporter: BacktraceReporting? ) { self.featureScope = featureScope + self.messageBus = messageBus self.globalAttributes = globalAttributes self.loggerAttributes = SynchronizedAttributes(attributes: [:]) self.loggerTags = SynchronizedTags(tags: []) @@ -193,17 +197,15 @@ internal final class RemoteLogger: LoggerProtocol, Sendable { busCombinedAttributes[Logs.Attributes.errorFingerprint] = errorFingerprint } - self.featureScope.send( - message: .payload( - RUMErrorMessage( - time: date, - message: log.error?.message ?? log.message, - source: "logger", - type: log.error?.kind, - stack: log.error?.stack, - attributes: busCombinedAttributes, - binaryImages: binaryImages - ) + self.messageBus.send( + message: RUMErrorMessage( + time: date, + message: log.error?.message ?? log.message, + source: "logger", + type: log.error?.kind, + stack: log.error?.stack, + attributes: busCombinedAttributes, + binaryImages: binaryImages ) ) } diff --git a/DatadogLogs/Tests/LogMessageReceiverTests.swift b/DatadogLogs/Tests/LogMessageReceiverTests.swift index 7fb320a608..51656ea848 100644 --- a/DatadogLogs/Tests/LogMessageReceiverTests.swift +++ b/DatadogLogs/Tests/LogMessageReceiverTests.swift @@ -13,27 +13,23 @@ class LogMessageReceiverTests: XCTestCase { func testReceivePartialLogMessage() throws { // Given let expectation = expectation(description: "Send log") - let core = PassthroughCoreMock( - context: .mockWith(service: "service-test"), - messageReceiver: LogMessageReceiver.mockAny() - ) + let core = PassthroughCoreMock(context: .mockWith(service: "service-test")) + core.subscribe(receiver: LogMessageReceiver.mockAny()) core.onEventWriteContext = { _ in expectation.fulfill() } // When - core.send( - message: .payload( - LogMessage( - logger: "logger-test", - service: nil, - date: .mockDecember15th2019At10AMUTC(), - message: "message-test", - error: nil, - level: .info, - thread: "thread-test", - networkInfoEnabled: nil, - userAttributes: nil, - internalAttributes: nil - ) + core.messageBus.send( + message: LogMessage( + logger: "logger-test", + service: nil, + date: .mockDecember15th2019At10AMUTC(), + message: "message-test", + error: nil, + level: .info, + thread: "thread-test", + networkInfoEnabled: nil, + userAttributes: nil, + internalAttributes: nil ) ) @@ -56,27 +52,23 @@ class LogMessageReceiverTests: XCTestCase { func testReceiveCompleteLogMessage() throws { // Given let expectation = expectation(description: "Send log") - let core = PassthroughCoreMock( - context: .mockAny(), - messageReceiver: LogMessageReceiver.mockAny() - ) + let core = PassthroughCoreMock(context: .mockAny()) + core.subscribe(receiver: LogMessageReceiver.mockAny()) core.onEventWriteContext = { _ in expectation.fulfill() } // When - core.send( - message: .payload( - LogMessage( - logger: "logger-test", - service: "service-test", - date: .mockDecember15th2019At10AMUTC(), - message: "message-test", - error: .mockAny(), - level: .info, - thread: "thread-test", - networkInfoEnabled: true, - userAttributes: ["user": "attribute"], - internalAttributes: ["internal": "attribute"] - ) + core.messageBus.send( + message: LogMessage( + logger: "logger-test", + service: "service-test", + date: .mockDecember15th2019At10AMUTC(), + message: "message-test", + error: .mockAny(), + level: .info, + thread: "thread-test", + networkInfoEnabled: true, + userAttributes: ["user": "attribute"], + internalAttributes: ["internal": "attribute"] ) ) @@ -105,29 +97,25 @@ class LogMessageReceiverTests: XCTestCase { func testReceiveRejectedLogMessage() throws { // Given let expectation = expectation(description: "Open scope but don't send log") - let core = PassthroughCoreMock( - context: .mockWith(service: "service-test"), - messageReceiver: LogMessageReceiver( - logEventMapper: SyncLogEventMapper { _ in nil } - ) - ) + let core = PassthroughCoreMock(context: .mockWith(service: "service-test")) + core.subscribe(receiver: LogMessageReceiver( + logEventMapper: SyncLogEventMapper { _ in nil } + )) core.onEventWriteContext = { _ in expectation.fulfill() } // When - core.send( - message: .payload( - LogMessage( - logger: "logger-test", - service: nil, - date: .mockDecember15th2019At10AMUTC(), - message: "message-test", - error: nil, - level: .info, - thread: "thread-test", - networkInfoEnabled: nil, - userAttributes: nil, - internalAttributes: nil - ) + core.messageBus.send( + message: LogMessage( + logger: "logger-test", + service: nil, + date: .mockDecember15th2019At10AMUTC(), + message: "message-test", + error: nil, + level: .info, + thread: "thread-test", + networkInfoEnabled: nil, + userAttributes: nil, + internalAttributes: nil ) ) diff --git a/DatadogLogs/Tests/LogsTests.swift b/DatadogLogs/Tests/LogsTests.swift index dda15e227d..695af9dc5e 100644 --- a/DatadogLogs/Tests/LogsTests.swift +++ b/DatadogLogs/Tests/LogsTests.swift @@ -128,12 +128,10 @@ class LogsTests: XCTestCase { func testItSendsGlobalLogUpdates_whenAddAttribute() throws { // Given - let mockMessageReceiver = FeatureMessageReceiverMock() - let core = SingleFeatureCoreMock( - messageReceiver: mockMessageReceiver - ) - let config = Logs.Configuration() - Logs.enable(with: config, in: core) + let core = SingleFeatureCoreMock() + Logs.enable(with: .init(), in: core) + var received: [LogEventAttributes] = [] + _ = core.messageBus.subscribe { (attrs: LogEventAttributes, _) in received.append(attrs) } // When let attributeKey: String = .mockRandom() @@ -141,20 +139,17 @@ class LogsTests: XCTestCase { Logs.addAttribute(forKey: attributeKey, value: attributeValue, in: core) // Then - let messages = mockMessageReceiver.messages.compactMap { $0.asPayload as? LogEventAttributes } - XCTAssertEqual(messages.count, 1) - let message = try XCTUnwrap(messages.first) + XCTAssertEqual(received.count, 1) + let message = try XCTUnwrap(received.first) XCTAssertEqual(message.attributes[attributeKey] as? String, attributeValue) } func testItSendsGlobalLogUpdates_whenRemoveAttribute() throws { // Given - let mockMessageReceiver = FeatureMessageReceiverMock() - let core = SingleFeatureCoreMock( - messageReceiver: mockMessageReceiver - ) - let config = Logs.Configuration() - Logs.enable(with: config, in: core) + let core = SingleFeatureCoreMock() + Logs.enable(with: .init(), in: core) + var received: [LogEventAttributes] = [] + _ = core.messageBus.subscribe { (attrs: LogEventAttributes, _) in received.append(attrs) } let attributeKey: String = .mockRandom() let attributeValue: String = .mockRandom() Logs.addAttribute(forKey: attributeKey, value: attributeValue, in: core) @@ -163,9 +158,8 @@ class LogsTests: XCTestCase { Logs.removeAttribute(forKey: attributeKey, in: core) // Then - let messages = mockMessageReceiver.messages.compactMap { $0.asPayload as? LogEventAttributes } - XCTAssertEqual(messages.count, 2) - let message = try XCTUnwrap(messages.last) + XCTAssertEqual(received.count, 2) + let message = try XCTUnwrap(received.last) XCTAssertNil(message.attributes[attributeKey]) } } diff --git a/DatadogLogs/Tests/RemoteLoggerTests.swift b/DatadogLogs/Tests/RemoteLoggerTests.swift index ca9fb9e2f2..c827acb870 100644 --- a/DatadogLogs/Tests/RemoteLoggerTests.swift +++ b/DatadogLogs/Tests/RemoteLoggerTests.swift @@ -9,8 +9,22 @@ import TestUtilities import DatadogInternal @testable import DatadogLogs +private final class RUMErrorMessageRecorder: BusMessageReceiver { + private(set) var messages: [RUMErrorMessage] = [] + func receive(message: RUMErrorMessage, from core: DatadogCoreProtocol) { + messages.append(message) + } +} + class RemoteLoggerTests: XCTestCase { private let featureScope = FeatureScopeMock() + private let messageBus = PassthroughCoreMock() + private let errorRecorder = RUMErrorMessageRecorder() + + override func setUp() { + super.setUp() + messageBus.subscribe(receiver: errorRecorder) + } // MARK: - Sending Error Message over Message Bus @@ -33,6 +47,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -45,13 +60,14 @@ class RemoteLoggerTests: XCTestCase { logger.info("Info message") // Then - XCTAssertEqual(featureScope.messagesSent().count, 0) + XCTAssertEqual(errorRecorder.messages.count, 0) } func testWhenErrorLogged_itPostsToMessageBus() throws { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -64,7 +80,7 @@ class RemoteLoggerTests: XCTestCase { logger.error("Error message") // Then - let errorMessage = try XCTUnwrap(featureScope.messagesSent().firstPayload as? RUMErrorMessage) + let errorMessage = try XCTUnwrap(errorRecorder.messages.first) XCTAssertEqual(errorMessage.message, "Error message") } @@ -72,6 +88,7 @@ class RemoteLoggerTests: XCTestCase { let stubBacktrace: BacktraceReport = .mockRandom() let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -84,7 +101,7 @@ class RemoteLoggerTests: XCTestCase { logger.error("Information message", error: ErrorMock(), attributes: [CrossPlatformAttributes.includeBinaryImages: true]) // Then - let errorMessage = try XCTUnwrap(featureScope.messagesSent().firstPayload as? RUMErrorMessage) + let errorMessage = try XCTUnwrap(errorRecorder.messages.first) // This is removed because binary images are sent in the message, so the additional attribute isn't needed XCTAssertNil(errorMessage.attributes[CrossPlatformAttributes.includeBinaryImages]) XCTAssertEqual(errorMessage.binaryImages?.count, stubBacktrace.binaryImages.count) @@ -104,6 +121,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -124,7 +142,7 @@ class RemoteLoggerTests: XCTestCase { ) // Then - let errorMessage = try XCTUnwrap(featureScope.messagesSent().firstPayload as? RUMErrorMessage) + let errorMessage = try XCTUnwrap(errorRecorder.messages.first) XCTAssertEqual(errorMessage.attributes[CrossPlatformAttributes.errorSourceType] as? String, "flutter") XCTAssertEqual(errorMessage.attributes[Logs.Attributes.errorFingerprint] as? String, mockFingerprint) } @@ -133,6 +151,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -156,7 +175,7 @@ class RemoteLoggerTests: XCTestCase { ) // Then - let errorMessage = try XCTUnwrap(featureScope.messagesSent().firstPayload as? RUMErrorMessage) + let errorMessage = try XCTUnwrap(errorRecorder.messages.first) XCTAssertEqual(errorMessage.attributes[CrossPlatformAttributes.errorSourceType] as? String, "flutter") XCTAssertEqual(errorMessage.attributes[Logs.Attributes.errorFingerprint] as? String, mockFingerprint) } @@ -169,6 +188,7 @@ class RemoteLoggerTests: XCTestCase { let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -214,6 +234,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -250,6 +271,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: SynchronizedAttributes(attributes: [attributeKey: attributeValue]), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -277,6 +299,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: SynchronizedAttributes(attributes: [attributeKey: globalAttributeValue]), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -306,6 +329,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: SynchronizedAttributes(attributes: [attributeKey: globalAttributeValue]), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -333,6 +357,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: SynchronizedAttributes(attributes: [attributeKey: attributeValue]), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -345,7 +370,7 @@ class RemoteLoggerTests: XCTestCase { logger.error("Error message") // Then - let errorMessage = try XCTUnwrap(featureScope.messagesSent().firstPayload as? RUMErrorMessage) + let errorMessage = try XCTUnwrap(errorRecorder.messages.first) XCTAssertEqual(errorMessage.attributes[attributeKey] as? String, attributeValue) } @@ -353,6 +378,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -378,6 +404,7 @@ class RemoteLoggerTests: XCTestCase { let stubBacktrace: BacktraceReport = .mockRandom() let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -415,6 +442,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -454,6 +482,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -497,6 +526,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -537,6 +567,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -566,6 +597,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -601,6 +633,7 @@ class RemoteLoggerTests: XCTestCase { // Given let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -638,6 +671,7 @@ class RemoteLoggerTests: XCTestCase { let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), @@ -685,6 +719,7 @@ class RemoteLoggerTests: XCTestCase { let logger = RemoteLogger( featureScope: featureScope, + messageBus: messageBus.messageBus, globalAttributes: .mockAny(), configuration: .mockAny(), dateProvider: RelativeDateProvider(), diff --git a/DatadogLogs/Tests/WebViewLogReceiverTests.swift b/DatadogLogs/Tests/WebViewLogReceiverTests.swift index 24506da2d1..eb2e18cda7 100644 --- a/DatadogLogs/Tests/WebViewLogReceiverTests.swift +++ b/DatadogLogs/Tests/WebViewLogReceiverTests.swift @@ -65,11 +65,9 @@ class WebViewLogReceiverTests: XCTestCase { let value: String = .mockRandom() // When - XCTAssert( - messageReceiver.receive( - message: .webview(.log(["test": value])), - from: core - ) + messageReceiver.receive( + message: WebViewLogMessage(event: ["test": value]), + from: core ) // Then @@ -117,11 +115,9 @@ class WebViewLogReceiverTests: XCTestCase { ] // When - XCTAssert( - messageReceiver.receive( - message: .webview(.log(webLogEvent)), - from: core - ) + messageReceiver.receive( + message: WebViewLogMessage(event: webLogEvent), + from: core ) // Then @@ -167,11 +163,9 @@ class WebViewLogReceiverTests: XCTestCase { core.onEventWriteContext = { _ in expectation.fulfill() } // When - XCTAssert( - messageReceiver.receive( - message: .webview(.log(["test": "value"])), - from: core - ) + messageReceiver.receive( + message: WebViewLogMessage(event: ["test": "value"]), + from: core ) // Then @@ -210,11 +204,9 @@ class WebViewLogReceiverTests: XCTestCase { core.onEventWriteContext = { _ in expectation.fulfill() } // When - XCTAssert( - messageReceiver.receive( - message: .webview(.log(["test": "value"])), - from: core - ) + messageReceiver.receive( + message: WebViewLogMessage(event: ["test": "value"]), + from: core ) // Then @@ -241,11 +233,9 @@ class WebViewLogReceiverTests: XCTestCase { core.onEventWriteContext = { _ in expectation.fulfill() } // When - XCTAssert( - messageReceiver.receive( - message: .webview(.log(["test": "value"])), - from: core - ) + messageReceiver.receive( + message: WebViewLogMessage(event: ["test": "value"]), + from: core ) // Then @@ -280,14 +270,12 @@ class WebViewLogReceiverTests: XCTestCase { core.onEventWriteContext = { _ in expectation.fulfill() } // When - XCTAssert( - messageReceiver.receive( - message: .webview(.log([ - "test": "value", - "usr": ["id": webUsrId, "name": webUsrName] - ])), - from: core - ) + messageReceiver.receive( + message: WebViewLogMessage(event: [ + "test": "value", + "usr": ["id": webUsrId, "name": webUsrName] + ]), + from: core ) // Then @@ -314,14 +302,12 @@ class WebViewLogReceiverTests: XCTestCase { core.onEventWriteContext = { _ in expectation.fulfill() } // When - XCTAssert( - messageReceiver.receive( - message: .webview(.log([ - "test": "value", - "usr": ["anonymous_id": browserAnonymousId] - ])), - from: core - ) + messageReceiver.receive( + message: WebViewLogMessage(event: [ + "test": "value", + "usr": ["anonymous_id": browserAnonymousId] + ]), + from: core ) // Then @@ -345,11 +331,9 @@ class WebViewLogReceiverTests: XCTestCase { core.onEventWriteContext = { _ in expectation.fulfill() } // When - XCTAssert( - messageReceiver.receive( - message: .webview(.log(["test": "value"])), - from: core - ) + messageReceiver.receive( + message: WebViewLogMessage(event: ["test": "value"]), + from: core ) // Then diff --git a/DatadogTrace/Sources/Feature/MessageReceivers.swift b/DatadogTrace/Sources/Feature/MessageReceivers.swift index 79abcbf288..2f374b5f99 100644 --- a/DatadogTrace/Sources/Feature/MessageReceivers.swift +++ b/DatadogTrace/Sources/Feature/MessageReceivers.swift @@ -21,7 +21,7 @@ internal struct CoreContext { var accountInfo: AccountInfo? } -internal final class ContextMessageReceiver: FeatureMessageReceiver { +internal final class ContextMessageReceiver: BusMessageReceiver { /// Creates a new `ContextMessageReceiver`. /// /// - parameters: @@ -41,24 +41,7 @@ internal final class ContextMessageReceiver: FeatureMessageReceiver { /// The tracer sampler that should be updated with the RUM deterministic sampler. let samplerProvider: SamplerProvider - /// Process messages receives from the bus. - /// - /// - Parameters: - /// - message: The Feature message - /// - core: The core from which the message is transmitted. - func receive(message: FeatureMessage, from core: DatadogCoreProtocol) -> Bool { - switch message { - case .context(let context): - return update(context: context, from: core) - default: - return false - } - } - - /// Updates context of the `DatadogTracer` if available. - /// - /// - Parameter context: The updated core context. - private func update(context datadogContext: DatadogContext, from core: DatadogCoreProtocol) -> Bool { + func receive(message datadogContext: DatadogContext, from core: DatadogCoreProtocol) { let rumContext = datadogContext.additionalContext(ofType: RUMCoreContext.self) _context.mutate { @@ -69,7 +52,5 @@ internal final class ContextMessageReceiver: FeatureMessageReceiver { } samplerProvider.updateWith(deterministicSampler: rumContext?.sessionSampler) - - return true } } diff --git a/DatadogTrace/Sources/Feature/TraceFeature.swift b/DatadogTrace/Sources/Feature/TraceFeature.swift index 45f2183a3f..91bc77ee8f 100644 --- a/DatadogTrace/Sources/Feature/TraceFeature.swift +++ b/DatadogTrace/Sources/Feature/TraceFeature.swift @@ -11,7 +11,7 @@ internal final class TraceFeature: DatadogRemoteFeature { static let name = "tracing" let requestBuilder: FeatureRequestBuilder - var messageReceiver: FeatureMessageReceiver { contextReceiver } + let messageReceiver: FeatureMessageReceiver = NOPFeatureMessageReceiver() let tracer: DatadogTracer let contextReceiver: ContextMessageReceiver diff --git a/DatadogTrace/Sources/Integrations/TracingWithLoggingIntegration.swift b/DatadogTrace/Sources/Integrations/TracingWithLoggingIntegration.swift index ade1b6beda..45ce2a1613 100644 --- a/DatadogTrace/Sources/Integrations/TracingWithLoggingIntegration.swift +++ b/DatadogTrace/Sources/Integrations/TracingWithLoggingIntegration.swift @@ -61,23 +61,21 @@ internal struct TracingWithLoggingIntegration { ) : nil - core.send( - message: .payload( - LogMessage( - logger: "trace", - service: service, - date: date, - message: message, - error: extractedError, - level: level, - thread: Thread.current.dd.name, - networkInfoEnabled: networkInfoEnabled, - userAttributes: userAttributes, - internalAttributes: [ - Constants.traceIDKey: String(spanContext.traceID, representation: .hexadecimal), - Constants.spanIDKey: String(spanContext.spanID, representation: .hexadecimal) - ] - ) + core.messageBus.send( + message: LogMessage( + logger: "trace", + service: service, + date: date, + message: message, + error: extractedError, + level: level, + thread: Thread.current.dd.name, + networkInfoEnabled: networkInfoEnabled, + userAttributes: userAttributes, + internalAttributes: [ + Constants.traceIDKey: String(spanContext.traceID, representation: .hexadecimal), + Constants.spanIDKey: String(spanContext.spanID, representation: .hexadecimal) + ] ), else: fallback ) diff --git a/DatadogTrace/Sources/Trace.swift b/DatadogTrace/Sources/Trace.swift index 375fd0577d..cd5ce1c719 100644 --- a/DatadogTrace/Sources/Trace.swift +++ b/DatadogTrace/Sources/Trace.swift @@ -42,6 +42,10 @@ public enum Trace { // Register Trace feature: let trace = TraceFeature(in: core, configuration: configuration) + + // Subscribe typed-bus receivers before registration so initial context push is received: + core.messageBus.subscribe(receiver: trace.contextReceiver) + try core.register(feature: trace) // If `URLSession` tracking is configured, register `URLSessionHandler` to enable distributed tracing: diff --git a/DatadogTrace/Tests/ContextMessageReceiverTests.swift b/DatadogTrace/Tests/ContextMessageReceiverTests.swift index 7d20e38fcb..5d635c8927 100644 --- a/DatadogTrace/Tests/ContextMessageReceiverTests.swift +++ b/DatadogTrace/Tests/ContextMessageReceiverTests.swift @@ -14,10 +14,8 @@ class ContextMessageReceiverTests: XCTestCase { func testItReceivesApplicationStateHistory() throws { // Given let receiver = ContextMessageReceiver(samplerProvider: SamplerProvider(sampleRate: .mockAny())) - let core = PassthroughCoreMock( - context: .mockWith(applicationStateHistory: .mockAppInBackground()), - messageReceiver: receiver - ) + let core = PassthroughCoreMock(context: .mockWith(applicationStateHistory: .mockAppInBackground())) + core.subscribe(receiver: receiver) XCTAssertEqual(receiver.context.applicationStateHistory?.currentState, .background) diff --git a/DatadogTrace/Tests/TracingURLSessionHandlerTests.swift b/DatadogTrace/Tests/TracingURLSessionHandlerTests.swift index 5c590de497..7947925ae8 100644 --- a/DatadogTrace/Tests/TracingURLSessionHandlerTests.swift +++ b/DatadogTrace/Tests/TracingURLSessionHandlerTests.swift @@ -20,7 +20,8 @@ class TracingURLSessionHandlerTests: XCTestCase { override func setUp() { super.setUp() let receiver = ContextMessageReceiver(samplerProvider: SamplerProvider(sampleRate: .mockAny())) - core = PassthroughCoreMock(messageReceiver: receiver) + core = PassthroughCoreMock() + core.subscribe(receiver: receiver) tracer = .mockWith( core: core, diff --git a/TestUtilities/Sources/Mocks/DatadogLogs/LoggingFeatureMocks.swift b/TestUtilities/Sources/Mocks/DatadogLogs/LoggingFeatureMocks.swift index c139b1015e..2f4f85baac 100644 --- a/TestUtilities/Sources/Mocks/DatadogLogs/LoggingFeatureMocks.swift +++ b/TestUtilities/Sources/Mocks/DatadogLogs/LoggingFeatureMocks.swift @@ -54,16 +54,14 @@ extension LogsFeature { } extension LogMessageReceiver: AnyMockable { - public static func mockAny() -> Self { - .mockWith() + public static func mockAny() -> LogMessageReceiver { + mockWith() } public static func mockWith( logEventMapper: LogEventMapper? = nil - ) -> Self { - .init( - logEventMapper: logEventMapper - ) + ) -> LogMessageReceiver { + LogMessageReceiver(logEventMapper: logEventMapper) } }