3434@property (nonatomic, strong) SentrySysctl *sysctl;
3535@property (nonatomic, assign) BOOL wasInBackground;
3636@property (nonatomic, strong) NSDate *didFinishLaunchingTimestamp;
37+ @property (nonatomic, assign) BOOL enablePreWarmedAppStartTracking;
3738
3839@end
3940
@@ -55,6 +56,7 @@ - (instancetype)initWithCurrentDateProvider:(id<SentryCurrentDateProvider>)curre
5556 dispatchQueueWrapper : (SentryDispatchQueueWrapper *)dispatchQueueWrapper
5657 appStateManager : (SentryAppStateManager *)appStateManager
5758 sysctl : (SentrySysctl *)sysctl
59+ enablePreWarmedAppStartTracking : (BOOL )enablePreWarmedAppStartTracking
5860{
5961 if (self = [super init ]) {
6062 self.currentDate = currentDateProvider;
@@ -64,6 +66,7 @@ - (instancetype)initWithCurrentDateProvider:(id<SentryCurrentDateProvider>)curre
6466 self.previousAppState = [self .appStateManager loadPreviousAppState ];
6567 self.wasInBackground = NO ;
6668 self.didFinishLaunchingTimestamp = [currentDateProvider date ];
69+ self.enablePreWarmedAppStartTracking = enablePreWarmedAppStartTracking;
6770 }
6871 return self;
6972}
@@ -120,12 +123,17 @@ - (void)buildAppStartMeasurement
120123 void (^block)(void ) = ^(void ) {
121124 [self stop ];
122125
123- // Don't (yet) report pre warmed app starts.
124- // Check if prewarm is available. Just to be safe to not drop app start data on earlier OS
125- // verions.
126+ BOOL isPreWarmed = NO ;
126127 if ([self isActivePrewarmAvailable ] && isActivePrewarm) {
127- SENTRY_LOG_INFO (@" The app was prewarmed. Not measuring app start." );
128- return ;
128+ SENTRY_LOG_INFO (@" The app was prewarmed." );
129+
130+ if (self.enablePreWarmedAppStartTracking ) {
131+ isPreWarmed = YES ;
132+ } else {
133+ SENTRY_LOG_INFO (
134+ @" EnablePreWarmedAppStartTracking disabled. Not measuring app start." );
135+ return ;
136+ }
129137 }
130138
131139 SentryAppStartType appStartType = [self getStartType ];
@@ -145,16 +153,29 @@ - (void)buildAppStartMeasurement
145153 // According to a talk at WWDC about optimizing app launch
146154 // (https://devstreaming-cdn.apple.com/videos/wwdc/2019/423lzf3qsjedrzivc7/423/423_optimizing_app_launch.pdf?dl=1
147155 // slide 17) no process exists for cold and warm launches. Since iOS 15, though, the system
148- // might decide to pre-warm your app before the user tries to open it. The process start
149- // time returned valid values when testing with real devices if the app start is not
150- // prewarmed. See:
156+ // might decide to pre-warm your app before the user tries to open it.
157+ // Prewarming can stop at any of the app launch steps. Our findings show that most of
158+ // the prewarmed app starts don't call the main method. Therefore we subtract the
159+ // time before the module initialization / main method to calculate the app start
160+ // duration. If the app start stopped during a later launch step, we drop it below with
161+ // checking the SENTRY_APP_START_MAX_DURATION. With this approach, we will
162+ // lose some warm app starts, but we accept this tradeoff. Useful resources:
151163 // https://developer.apple.com/documentation/uikit/app_and_environment/responding_to_the_launch_of_your_app/about_the_app_launch_sequence#3894431
152164 // https://developer.apple.com/documentation/metrickit/mxapplaunchmetric,
153165 // https://twitter.com/steipete/status/1466013492180312068,
154166 // https://github.com/MobileNativeFoundation/discussions/discussions/146
155-
156- NSTimeInterval appStartDuration =
157- [[self .currentDate date ] timeIntervalSinceDate: self .sysctl.processStartTimestamp];
167+ // https://eisel.me/startup
168+ NSTimeInterval appStartDuration = 0.0 ;
169+ NSDate *appStartTimestamp;
170+ if (isPreWarmed) {
171+ appStartDuration = [[self .currentDate date ]
172+ timeIntervalSinceDate: self .sysctl.moduleInitializationTimestamp];
173+ appStartTimestamp = self.sysctl .moduleInitializationTimestamp ;
174+ } else {
175+ appStartDuration =
176+ [[self .currentDate date ] timeIntervalSinceDate: self .sysctl.processStartTimestamp];
177+ appStartTimestamp = self.sysctl .processStartTimestamp ;
178+ }
158179
159180 // Safety check to not report app starts that are completely off.
160181 if (appStartDuration >= SENTRY_APP_START_MAX_DURATION) {
@@ -177,7 +198,8 @@ - (void)buildAppStartMeasurement
177198
178199 SentryAppStartMeasurement *appStartMeasurement = [[SentryAppStartMeasurement alloc ]
179200 initWithType: appStartType
180- appStartTimestamp: self .sysctl.processStartTimestamp
201+ isPreWarmed: isPreWarmed
202+ appStartTimestamp: appStartTimestamp
181203 duration: appStartDuration
182204 runtimeInitTimestamp: runtimeInit
183205 moduleInitializationTimestamp: self .sysctl.moduleInitializationTimestamp
0 commit comments