diff --git a/benchmarks/android/app/build.gradle b/benchmarks/android/app/build.gradle index af90679f6..1a4f94b95 100644 --- a/benchmarks/android/app/build.gradle +++ b/benchmarks/android/app/build.gradle @@ -129,7 +129,7 @@ dependencies { // Benchmark tools from dd-sdk-android are used for vitals recording // Remember to bump this alongside the main dd-sdk-android dependencies - implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.13.1") + implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.15.0") // Required for compile-time access to Datadog.getInstance() required by BenchmarkVitalsModuleImpl - implementation("com.datadoghq:dd-sdk-android-core:3.13.1") + implementation("com.datadoghq:dd-sdk-android-core:3.15.0") } diff --git a/benchmarks/ios/BenchmarkRunner.xcodeproj/project.pbxproj b/benchmarks/ios/BenchmarkRunner.xcodeproj/project.pbxproj index d845ae407..bafa77e62 100644 --- a/benchmarks/ios/BenchmarkRunner.xcodeproj/project.pbxproj +++ b/benchmarks/ios/BenchmarkRunner.xcodeproj/project.pbxproj @@ -582,6 +582,7 @@ LIBRARY_SEARCH_PATHS = ( "\"$(SDKROOT)/usr/lib/swift\"", "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$(PLATFORM_NAME)\"", "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = YES; @@ -655,6 +656,7 @@ LIBRARY_SEARCH_PATHS = ( "\"$(SDKROOT)/usr/lib/swift\"", "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$(PLATFORM_NAME)\"", "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = NO; diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index b67a4d00d..eba05292b 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -2155,7 +2155,7 @@ SPEC CHECKSUMS: KSCrash: 8c4464fd5da7de520f2ce4a00fdf63f169a80f18 OpenTelemetry-Swift-Api: 3be9043f6288eb4ac3cbe7548b5bd45fb4ed5849 OpenTelemetry-Swift-Sdk: d9fa7bc839350f5a81467a5e877f727a8a77712d - RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809 + RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82 RCTDeprecation: be794de7dc6ed8f9f7fbf525f86e7651b8b68746 RCTRequired: a83787b092ec554c2eb6019ff3f5b8d125472b3b RCTTypeSafety: 48ad3c858926b1c46f46a81a58822b476e178e2c diff --git a/example-new-architecture/App.tsx b/example-new-architecture/App.tsx index f753110f7..db326f6d4 100644 --- a/example-new-architecture/App.tsx +++ b/example-new-architecture/App.tsx @@ -92,6 +92,10 @@ import {APPLICATION_ID, CLIENT_TOKEN, ENVIRONMENT} from './ddCredentials'; const spanId = await DdTrace.startSpan('test span'); await DdTrace.finishSpan(spanId); + + setTimeout(async () => { + await DdRum.reportAppFullyDisplayed(); + }, 5000); })(); function AppWithProviders() { diff --git a/example-new-architecture/ios/DdSdkReactNativeExample.xcodeproj/project.pbxproj b/example-new-architecture/ios/DdSdkReactNativeExample.xcodeproj/project.pbxproj index ce12a7261..73e40590a 100644 --- a/example-new-architecture/ios/DdSdkReactNativeExample.xcodeproj/project.pbxproj +++ b/example-new-architecture/ios/DdSdkReactNativeExample.xcodeproj/project.pbxproj @@ -412,6 +412,7 @@ LIBRARY_SEARCH_PATHS = ( "\"$(SDKROOT)/usr/lib/swift\"", "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$(PLATFORM_NAME)\"", "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = YES; @@ -492,6 +493,7 @@ LIBRARY_SEARCH_PATHS = ( "\"$(SDKROOT)/usr/lib/swift\"", "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$(PLATFORM_NAME)\"", "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = NO; diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index d4cb5b5c8..fcc53542d 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -1883,7 +1883,7 @@ SPEC CHECKSUMS: hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11 KSCrash: 8c4464fd5da7de520f2ce4a00fdf63f169a80f18 OpenTelemetry-Swift-Api: 3be9043f6288eb4ac3cbe7548b5bd45fb4ed5849 - RCT-Folly: 7b4f73a92ad9571b9dbdb05bb30fad927fa971e1 + RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17 RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83 RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716 RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f diff --git a/example/ios/DdSdkReactNativeExample.xcodeproj/project.pbxproj b/example/ios/DdSdkReactNativeExample.xcodeproj/project.pbxproj index bde88ccb4..f0dddcbe5 100644 --- a/example/ios/DdSdkReactNativeExample.xcodeproj/project.pbxproj +++ b/example/ios/DdSdkReactNativeExample.xcodeproj/project.pbxproj @@ -398,6 +398,7 @@ LIBRARY_SEARCH_PATHS = ( "\"$(SDKROOT)/usr/lib/swift\"", "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$(PLATFORM_NAME)\"", "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = YES; @@ -475,6 +476,7 @@ LIBRARY_SEARCH_PATHS = ( "\"$(SDKROOT)/usr/lib/swift\"", "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(DEVELOPER_DIR)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$(PLATFORM_NAME)\"", "\"$(inherited)\"", ); MTL_ENABLE_DEBUG_INFO = NO; diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index a8d923aa8..f72ec23b0 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1987,7 +1987,7 @@ SPEC CHECKSUMS: HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352 KSCrash: 8c4464fd5da7de520f2ce4a00fdf63f169a80f18 OpenTelemetry-Swift-Api: 3be9043f6288eb4ac3cbe7548b5bd45fb4ed5849 - RCT-Folly: 7b4f73a92ad9571b9dbdb05bb30fad927fa971e1 + RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17 RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83 RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716 RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f diff --git a/example/src/App.tsx b/example/src/App.tsx index dc2f6acb5..95546c6df 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -8,7 +8,7 @@ import TraceScreen from './screens/TraceScreen'; import style from './screens/styles'; import { navigationRef } from './NavigationRoot'; import { DdRumReactNavigationTracking, NavigationTrackingOptions, ParamsTrackingPredicate, ViewNamePredicate, ViewTrackingPredicate } from '@datadog/mobile-react-navigation'; -import { DatadogProvider, TrackingConsent, DdFlags, DdLogs, DdSdkReactNative } from '@datadog/mobile-react-native' +import { DdSdkReactNative, DatadogProvider, TrackingConsent, DdLogs, DdRum, DdFlags } from '@datadog/mobile-react-native' import { OpenFeatureProvider } from '@openfeature/react-sdk'; import { ImagePrivacyLevel, @@ -96,6 +96,10 @@ const handleDatadogInitialization = async () => { // ConfigurationWire, no network); the "Flags source" switch on the Home screen flips to // the online provider (CDN) at runtime. await setFlagsProvider('offline'); + + setTimeout(async () => { + await DdRum.reportAppFullyDisplayed(); + }, 5000); } export default function App() { diff --git a/example/src/ddUtils.tsx b/example/src/ddUtils.tsx index 4933cec10..d4acbe069 100644 --- a/example/src/ddUtils.tsx +++ b/example/src/ddUtils.tsx @@ -1,6 +1,7 @@ import { DatadogProviderConfiguration, DdLogs, + DdRum, DdSdkReactNative, CoreConfiguration, SdkVerbosity, @@ -87,6 +88,10 @@ export function initializeDatadog(trackingConsent: TrackingConsent) { DdLogs.info('The RN Sdk was properly initialized') DdSdkReactNative.setUserInfo({id: "1337", name: "Xavier", email: "xg@example.com", extraInfo: { type: "premium" } }) DdSdkReactNative.addAttributes({campaign: "ad-network"}) + + setTimeout(async () => { + await DdRum.reportAppFullyDisplayed(); + }, 5000); }); // Enable the Flags feature. diff --git a/packages/core/__mocks__/react-native.ts b/packages/core/__mocks__/react-native.ts index 22f771f99..7c4157747 100644 --- a/packages/core/__mocks__/react-native.ts +++ b/packages/core/__mocks__/react-native.ts @@ -162,6 +162,9 @@ actualRN.NativeModules.DdRum = { addViewLoadingTime: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, + reportAppFullyDisplayed: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, stopSession: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index 1b3238fa2..c95ee2b16 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -213,7 +213,7 @@ dependencies { // This breaks builds if the React Native target is below 0.76.0. as it relies on Gradle 8.5.0. // To avoid this, we enforce 1.0.0-beta01 on RN < 0.76.0 if (reactNativeMinorVersion < 76) { - implementation("com.datadoghq:dd-sdk-android-rum:3.13.1") { + implementation("com.datadoghq:dd-sdk-android-rum:3.15.0") { exclude group: "androidx.metrics", module: "metrics-performance" } implementation "androidx.metrics:metrics-performance:1.0.0-beta01" @@ -233,14 +233,15 @@ dependencies { } } } else { - implementation "com.datadoghq:dd-sdk-android-rum:3.13.1" + implementation "com.datadoghq:dd-sdk-android-rum:3.15.0" } - implementation "com.datadoghq:dd-sdk-android-logs:3.13.1" - implementation "com.datadoghq:dd-sdk-android-trace:3.13.1" - implementation "com.datadoghq:dd-sdk-android-webview:3.13.1" - implementation "com.datadoghq:dd-sdk-android-ndk:3.13.1" - implementation "com.datadoghq:dd-sdk-android-flags:3.13.1" - implementation "com.datadoghq:dd-sdk-android-internal:3.13.1" + implementation "com.datadoghq:dd-sdk-android-logs:3.15.0" + implementation "com.datadoghq:dd-sdk-android-trace:3.15.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.15.0" + implementation "com.datadoghq:dd-sdk-android-ndk:3.15.0" + implementation "com.datadoghq:dd-sdk-android-flags:3.15.0" + implementation "com.datadoghq:dd-sdk-android-internal:3.15.0" + implementation "com.datadoghq:dd-sdk-android-rum-prelaunch:3.15.0" implementation "com.google.code.gson:gson:2.11.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt index e090f5338..8a09527ec 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt @@ -327,6 +327,16 @@ class DdRumImplementation internal constructor( promise.resolve(null) } + /** + * This method can be used to mark the moment in time when the UI of the app is considered fully displayed. + * The duration between the application launch and this moment of time will be shown as TTFD (time to full display) + * in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session. + */ + fun reportAppFullyDisplayed(promise: Promise) { + datadog.getRumMonitor().reportAppFullyDisplayed() + promise.resolve(null) + } + /** * Stops the current RUM Session. */ diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt index 72baeecdd..2d31c8c4b 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt @@ -276,6 +276,16 @@ class DdRum( implementation.addViewLoadingTime(overwrite, promise) } + /** + * This method can be used to mark the moment in time when the UI of the app is considered fully displayed. + * The duration between the application launch and this moment of time will be shown as TTFD (time to full display) + * in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session. + */ + @ReactMethod + override fun reportAppFullyDisplayed(promise: Promise) { + implementation.reportAppFullyDisplayed(promise) + } + /** * Stops the current RUM Session. */ diff --git a/packages/core/android/src/oldarchCommon/kotlin/com/datadog/reactnative/DdRum.kt b/packages/core/android/src/oldarchCommon/kotlin/com/datadog/reactnative/DdRum.kt index 79f7ad06a..327538dbb 100644 --- a/packages/core/android/src/oldarchCommon/kotlin/com/datadog/reactnative/DdRum.kt +++ b/packages/core/android/src/oldarchCommon/kotlin/com/datadog/reactnative/DdRum.kt @@ -258,6 +258,16 @@ class DdRum( implementation.addViewLoadingTime(overwrite, promise) } + /** + * This method can be used to mark the moment in time when the UI of the app is considered fully displayed. + * The duration between the application launch and this moment of time will be shown as TTFD (time to full display) + * in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session. + */ + @ReactMethod + fun reportAppFullyDisplayed(promise: Promise) { + implementation.reportAppFullyDisplayed(promise) + } + /** * Stops the current RUM Session. */ diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt index b03c377d8..ac007343b 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt @@ -557,6 +557,15 @@ internal class DdRumTest { verify(mockRumMonitor).addViewLoadingTime(overwrite) } + @Test + fun `M call reportAppFullyDisplayed w reportAppFullyDisplayed()`() { + // When + testedDdRum.reportAppFullyDisplayed(mockPromise) + + // Then + verify(mockRumMonitor).reportAppFullyDisplayed() + } + @Test fun `M call stopSession W stopSession()`() { // When diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt index df0e8b5ad..5b52a9733 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt @@ -63,6 +63,9 @@ class MockRumMonitor : RumMonitor { @ExperimentalRumApi override fun addViewLoadingTime(overwrite: Boolean) {} + @ExperimentalRumApi + override fun reportAppFullyDisplayed() {} + override fun getCurrentSessionId(callback: (String?) -> Unit) {} override fun addViewAttributes(attributes: Map) {} @@ -172,7 +175,4 @@ class MockRumMonitor : RumMonitor { failureReason: OperationFailureReason, attributes: Map ) {} - - @ExperimentalRumApi - override fun reportAppFullyDisplayed() {} } diff --git a/packages/core/ios/Sources/DdRum.mm b/packages/core/ios/Sources/DdRum.mm index 58c9938cf..72ecff9c6 100644 --- a/packages/core/ios/Sources/DdRum.mm +++ b/packages/core/ios/Sources/DdRum.mm @@ -145,7 +145,15 @@ @implementation DdRum [self addViewLoadingTime:overwrite resolve:resolve reject:reject]; } -RCT_REMAP_METHOD(stopSession, withResolve:(RCTPromiseResolveBlock)resolve +RCT_EXPORT_METHOD(reportAppFullyDisplayed: + (RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self reportAppFullyDisplayed: resolve reject:reject]; +} + +RCT_EXPORT_METHOD(stopSession: + (RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) { [self stopSession:resolve reject:reject]; @@ -262,6 +270,10 @@ - (void)addViewLoadingTime:(BOOL)overwrite resolve:(RCTPromiseResolveBlock)resol [self.ddRumImplementation addViewLoadingTimeWithOverwrite:overwrite resolve:resolve reject:reject]; } +- (void)reportAppFullyDisplayed:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation reportAppFullyDisplayedWithResolve:resolve reject: reject]; +} + - (void)startAction:(NSString *)type name:(NSString *)name context:(NSDictionary *)context timestampMs:(double)timestampMs resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self.ddRumImplementation startActionWithType:type name:name context:context timestampMs:timestampMs resolve:resolve reject:reject]; } diff --git a/packages/core/ios/Sources/DdRumImplementation.swift b/packages/core/ios/Sources/DdRumImplementation.swift index 97ff8d468..60d34b74a 100644 --- a/packages/core/ios/Sources/DdRumImplementation.swift +++ b/packages/core/ios/Sources/DdRumImplementation.swift @@ -262,6 +262,12 @@ public class DdRumImplementation: NSObject { resolve(nil) } + @objc + public func reportAppFullyDisplayed(resolve: @escaping RCTPromiseResolve, reject: @escaping RCTPromiseReject) -> Void { + nativeRUM.reportAppFullyDisplayed() + resolve(nil) + } + @objc public func stopSession(resolve: @escaping RCTPromiseResolve, reject: @escaping RCTPromiseReject) -> Void { nativeRUM.stopSession() diff --git a/packages/core/ios/Tests/DdRumTests.swift b/packages/core/ios/Tests/DdRumTests.swift index 3a8fca2e6..bae648be5 100644 --- a/packages/core/ios/Tests/DdRumTests.swift +++ b/packages/core/ios/Tests/DdRumTests.swift @@ -481,6 +481,14 @@ internal class DdRumTests: XCTestCase { XCTAssertEqual(mockNativeRUM.receivedAttributes.count, 0) } + func testReportAppFullyDisplayed() throws { + rum.reportAppFullyDisplayed(resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(mockNativeRUM.calledMethods.count, 1) + XCTAssertEqual(mockNativeRUM.calledMethods.last, .reportAppFullyDisplayed()) + XCTAssertEqual(mockNativeRUM.receivedAttributes.count, 0) + } + func testStopSession() throws { rum.stopSession(resolve: mockResolve, reject: mockReject) diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index 3b25d9279..24d934933 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -11,10 +11,6 @@ import Foundation @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { - func reportAppFullyDisplayed() { - // not implemented - } - func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } @@ -70,6 +66,7 @@ internal class MockRUMMonitor: RUMMonitorProtocol { case addViewAttributes(_: Int? = nil) // We need an attribute for the case to be Equatable case removeViewAttributes(keys: [String]) case addViewLoadingTime(overwrite: Bool) + case reportAppFullyDisplayed(_: Int? = nil) // We need an attribute for the case to be Equatable case stopSession(_: Int? = nil) // We need an attribute for the case to be Equatable case addResourceMetrics(resourceKey: String, fetch: Interval, @@ -152,6 +149,9 @@ internal class MockRUMMonitor: RUMMonitorProtocol { func addViewLoadingTime(overwrite: Bool) { calledMethods.append(.addViewLoadingTime(overwrite: overwrite)) } + func reportAppFullyDisplayed() { + calledMethods.append(.reportAppFullyDisplayed()) + } func stopSession() { calledMethods.append(.stopSession()) } diff --git a/packages/core/jest/mock.js b/packages/core/jest/mock.js index aadc79d27..4d91a73c4 100644 --- a/packages/core/jest/mock.js +++ b/packages/core/jest/mock.js @@ -143,6 +143,9 @@ module.exports = { addViewLoadingTime: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), + reportAppFullyDisplayed: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), addFeatureFlagEvaluation: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index a9cfb9f9f..10db74599 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -427,6 +427,16 @@ class DdRumWrapper implements DdRumType { ); }; + reportAppFullyDisplayed = (): Promise => { + InternalLog.log( + 'Reporting App Fully Displayed (TTFD)', + SdkVerbosity.DEBUG + ); + return bufferVoidNativeCall(() => + this.nativeRum.reportAppFullyDisplayed() + ); + }; + stopSession = (): Promise => { InternalLog.log('Stopping RUM Session', SdkVerbosity.DEBUG); clearCachedSessionId(); diff --git a/packages/core/src/rum/__tests__/DdRum.test.ts b/packages/core/src/rum/__tests__/DdRum.test.ts index dc5c64bae..ea0fccf67 100644 --- a/packages/core/src/rum/__tests__/DdRum.test.ts +++ b/packages/core/src/rum/__tests__/DdRum.test.ts @@ -1884,6 +1884,16 @@ describe('DdRum', () => { }); }); + describe('DdRum.reportAppFullyDisplayed', () => { + it('calls the native API', async () => { + await DdRum.reportAppFullyDisplayed(); + + expect( + NativeModules.DdRum.reportAppFullyDisplayed + ).toHaveBeenCalledTimes(1); + }); + }); + describe('PropagatorTypes', () => { it('matches with the native name of propagators', () => { /** diff --git a/packages/core/src/rum/types.ts b/packages/core/src/rum/types.ts index b879010f7..8b173376a 100644 --- a/packages/core/src/rum/types.ts +++ b/packages/core/src/rum/types.ts @@ -181,6 +181,13 @@ export type DdRumType = { */ addViewLoadingTime(overwrite: boolean): Promise; + /** + * This method can be used to mark the moment in time when the UI of the app is considered fully displayed. + * The duration between the application launch and this moment of time will be shown as TTFD (time to full display) + * in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session. + */ + reportAppFullyDisplayed(): Promise; + /** * Stops the current RUM Session. */ diff --git a/packages/core/src/specs/NativeDdRum.ts b/packages/core/src/specs/NativeDdRum.ts index dbe417f44..00950807a 100644 --- a/packages/core/src/specs/NativeDdRum.ts +++ b/packages/core/src/specs/NativeDdRum.ts @@ -170,6 +170,13 @@ export interface Spec extends TurboModule { */ addViewLoadingTime(overwrite: boolean): Promise; + /** + * This method can be used to mark the moment in time when the UI of the app is considered fully displayed. + * The duration between the application launch and this moment of time will be shown as TTFD (time to full display) + * in the RUM session explorer. Only the first call to this method will have any effect for a given RUM session. + */ + reportAppFullyDisplayed(): Promise; + /** * Stops the current RUM Session. */ diff --git a/packages/internal-testing-tools/android/build.gradle b/packages/internal-testing-tools/android/build.gradle index 276d3e508..fe8296905 100644 --- a/packages/internal-testing-tools/android/build.gradle +++ b/packages/internal-testing-tools/android/build.gradle @@ -188,7 +188,7 @@ dependencies { } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(path: ':datadog_mobile-react-native') - implementation "com.datadoghq:dd-sdk-android-core:3.13.1" + implementation "com.datadoghq:dd-sdk-android-core:3.15.0" implementation "com.google.code.gson:gson:2.11.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" diff --git a/packages/react-native-session-replay/android/build.gradle b/packages/react-native-session-replay/android/build.gradle index f9f1b3da3..8c8fee867 100644 --- a/packages/react-native-session-replay/android/build.gradle +++ b/packages/react-native-session-replay/android/build.gradle @@ -188,7 +188,6 @@ repositories { mavenCentral() google() maven { url "https://jitpack.io" } - mavenLocal() if (reactNativeMajorVersion == 0 && reactNativeMinorVersion < 71) { def androidSourcesDir = file("$reactNativeRootDir/android") @@ -216,8 +215,8 @@ dependencies { api "com.facebook.react:react-android:$reactNativeVersion" } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.datadoghq:dd-sdk-android-session-replay:3.13.1" - implementation "com.datadoghq:dd-sdk-android-internal:3.13.1" + implementation "com.datadoghq:dd-sdk-android-session-replay:3.15.0" + implementation "com.datadoghq:dd-sdk-android-internal:3.15.0" implementation project(path: ':datadog_mobile-react-native') testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" diff --git a/packages/react-native-webview/android/build.gradle b/packages/react-native-webview/android/build.gradle index e145fb2cd..7790c4335 100644 --- a/packages/react-native-webview/android/build.gradle +++ b/packages/react-native-webview/android/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "com.facebook.react:react-android:$reactNativeVersion" } - implementation "com.datadoghq:dd-sdk-android-webview:3.13.1" + implementation "com.datadoghq:dd-sdk-android-webview:3.15.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(path: ':datadog_mobile-react-native')