@@ -21,7 +21,11 @@ internal class RUMAppLaunchManager {
2121 private let telemetryController : AppLaunchMetricController
2222
2323 private var timeToInitialDisplay : Double ?
24- private var timeToFullDisplay : Double ?
24+ private var timeToFullDisplay : (
25+ duration: TimeInterval ,
26+ vitalId: String ,
27+ attributes: [ AttributeKey : AttributeValue ]
28+ ) ?
2529 private var startupType : RUMVitalAppLaunchEvent . Vital . StartupType ?
2630
2731 private lazy var startupTypeHandler = StartupTypeHandler ( telemetryController: telemetryController)
@@ -63,18 +67,29 @@ private extension RUMAppLaunchManager {
6367
6468 self . timeToInitialDisplay = ttid
6569 let ttidVitalId = dependencies. rumUUIDGenerator. generateUnique ( ) . toRUMDataFormat
66-
6770 let profiling = context. additionalContext ( ofType: ProfilingContext . self) ? . ddProfiling
6871
72+ if let timeToFullDisplay {
73+ let ttfdVital = Vital (
74+ id: timeToFullDisplay. vitalId,
75+ name: RUMVitalAppLaunchEvent . Vital. AppLaunchMetric. ttfd. name,
76+ date: context. launchInfo. processLaunchDate,
77+ serverTimeOffset: context. serverTimeOffset,
78+ duration: max ( ttid, timeToFullDisplay. duration) . dd. toInt64Nanoseconds
79+ )
80+
81+ // TTID closes app-launch profiling, so only a TTFD reported earlier is sent here.
82+ sendTTFDMessageToProfiler ( vital: ttfdVital)
83+ }
84+
6985 sendTTIDMessageToProfiler (
7086 vital: . init(
7187 id: ttidVitalId,
7288 name: RUMVitalAppLaunchEvent . Vital. AppLaunchMetric. ttid. name,
7389 date: context. launchInfo. processLaunchDate,
7490 serverTimeOffset: context. serverTimeOffset,
7591 duration: ttid. dd. toInt64Nanoseconds
76- ) ,
77- activeView: activeView
92+ )
7893 )
7994
8095 dependencies. appStateManager. previousAppStateInfo { [ weak self] previousAppStateInfo in
@@ -103,19 +118,20 @@ private extension RUMAppLaunchManager {
103118
104119 // The TTFD is always written after the TTID. If it exists already, means it was not written before.
105120 if let timeToFullDisplay {
106- let ttfd = max ( ttid, timeToFullDisplay)
121+ let ttfd = max ( ttid, timeToFullDisplay. duration )
107122 self . writeVitalEvent (
108- vitalId: dependencies . rumUUIDGenerator . generateUnique ( ) . toRUMDataFormat ,
123+ vitalId: timeToFullDisplay . vitalId ,
109124 duration: Double ( ttfd. dd. toInt64Nanoseconds) ,
110125 appLaunchMetric: . ttfd,
111126 startupType: startupType,
112- attributes: attributes,
127+ attributes: timeToFullDisplay . attributes,
113128 context: context,
114129 writer: writer,
115- activeView: activeView
130+ activeView: activeView,
131+ profiling: profiling
116132 )
117133
118- telemetryController. trackTTFD ( duration: timeToFullDisplay . dd. toInt64Nanoseconds)
134+ telemetryController. trackTTFD ( duration: ttfd . dd. toInt64Nanoseconds)
119135 }
120136
121137 telemetryController. sendMetric ( )
@@ -224,10 +240,8 @@ private extension RUMAppLaunchManager {
224240 telemetryController. track ( ttidEvent: vitalEvent, context: context)
225241 }
226242
227- func sendTTIDMessageToProfiler( vital: Vital , activeView: RUMViewScope ? ) {
228- var contextAttributes : [ String : Encodable ] = parent. rumContextAttributes
229- contextAttributes [ RUMCoreContext . IDs. vitalID] = vital. id
230-
243+ func sendTTIDMessageToProfiler( vital: Vital ) {
244+ let contextAttributes : [ String : Encodable ] = parent. rumContextAttributes
231245 dependencies. featureScope. send ( message: . payload( TTIDMessage ( attributes: contextAttributes, ttid: vital) ) )
232246 }
233247}
@@ -239,23 +253,38 @@ private extension RUMAppLaunchManager {
239253 guard shouldProcess ( command: command, context: context) ,
240254 let ttfd = time ( from: command, context: context) else { return }
241255
242- self . timeToFullDisplay = ttfd
243-
244- if let timeToFullDisplay , let timeToInitialDisplay , let startupType {
245- let attributes = command. globalAttributes
246- . merging ( command . attributes ) { $1 }
247- let ttfd = max ( timeToInitialDisplay , timeToFullDisplay)
256+ let timeToFullDisplay = (
257+ duration : timeToInitialDisplay . map { max ( $0 , ttfd ) } ?? ttfd ,
258+ vitalId : dependencies . rumUUIDGenerator . generateUnique ( ) . toRUMDataFormat ,
259+ attributes: command. globalAttributes. merging ( command . attributes ) { $1 }
260+ )
261+ self . timeToFullDisplay = timeToFullDisplay
248262
249- self . writeVitalEvent (
250- vitalId: dependencies. rumUUIDGenerator. generateUnique ( ) . toRUMDataFormat,
251- duration: Double ( ttfd. dd. toInt64Nanoseconds) ,
252- appLaunchMetric: . ttfd,
253- startupType: startupType,
254- attributes: attributes,
255- context: context,
256- writer: writer,
257- activeView: activeView
263+ if timeToInitialDisplay != nil {
264+ let ttfdVital = Vital (
265+ id: timeToFullDisplay. vitalId,
266+ name: RUMVitalAppLaunchEvent . Vital. AppLaunchMetric. ttfd. name,
267+ date: context. launchInfo. processLaunchDate,
268+ serverTimeOffset: context. serverTimeOffset,
269+ duration: timeToFullDisplay. duration. dd. toInt64Nanoseconds
258270 )
271+ sendTTFDMessageToProfiler ( vital: ttfdVital)
272+
273+ if let startupType {
274+ let profiling = context. additionalContext ( ofType: ProfilingContext . self) ? . ddProfiling
275+
276+ self . writeVitalEvent (
277+ vitalId: ttfdVital. id,
278+ duration: Double ( timeToFullDisplay. duration. dd. toInt64Nanoseconds) ,
279+ appLaunchMetric: . ttfd,
280+ startupType: startupType,
281+ attributes: timeToFullDisplay. attributes,
282+ context: context,
283+ writer: writer,
284+ activeView: activeView,
285+ profiling: profiling
286+ )
287+ }
259288 }
260289 }
261290
@@ -274,6 +303,11 @@ private extension RUMAppLaunchManager {
274303
275304 return true
276305 }
306+
307+ func sendTTFDMessageToProfiler( vital: Vital ) {
308+ let contextAttributes : [ String : Encodable ] = parent. rumContextAttributes
309+ dependencies. featureScope. send ( message: . payload( OperationMessage ( attributes: contextAttributes, operation: vital) ) )
310+ }
277311}
278312
279313private extension RUMVitalAppLaunchEvent . Vital . AppLaunchMetric {
0 commit comments