Monitoring App Launch Time (Launch Time Tracking)
Cold startup time is one of the few metrics users notice before seeing any screen. Apple rejects apps with cold launch > 20 seconds, but real perception threshold is around 2 seconds. If your metrics say "average launch 1.2 sec" but P95 is 4.8 seconds, one in twenty users waits almost five seconds. Without monitoring, this tail is invisible.
Launch Types
Cold launch — app not in memory, process created from scratch. Slowest, most important to monitor.
Warm launch (iOS) — app was in memory but dumped to background. Process alive, but viewDidLoad executes again.
Hot launch — return from background. Nearly instant. Don't confuse: hot doesn't need re-init.
Monitor cold and warm. Hot isn't indicative.
Built-in Platform Tools
iOS — MetricKit. Starting iOS 13, the system aggregates real-user diagnostics and provides via MXMetricManager:
class AppDelegate: MXMetricManagerSubscriber {
func applicationDidFinishLaunching() {
MXMetricManager.shared.add(self)
}
func didReceive(_ payloads: [MXMetricPayload]) {
for payload in payloads {
if let launchMetric = payload.applicationLaunchMetrics {
let coldLaunchP50 = launchMetric.histogrammedTimeToFirstDrawKey
.histogram(for: .applicationLaunchTimeToFirstDraw)
// Send to analytics
Analytics.track("cold_launch_p50", value: coldLaunchP50)
}
}
}
}
MetricKit delivers data once daily, aggregated over previous 24 hours. Not real-time, but real sample across all users.
Android — Firebase Performance Monitoring. app_start trace collects automatically on SDK connection. Splits app_start_cold and app_start_warm. Available in Firebase Console with breakdown by devices, OS versions, app versions.
For custom markers in Android—FirebasePerformance.getInstance().newTrace("custom_init") + start() / stop(). Understand which initialization bogs down.
Manual Instrumentation
Even without external SDKs, measure startup yourself.
iOS:
// In AppDelegate or @main
static let appLaunchTimestamp = Date()
// In viewDidAppear of first screen
let launchDuration = Date().timeIntervalSince(AppDelegate.appLaunchTimestamp)
Analytics.track("cold_launch_duration", value: launchDuration)
But this is imprecise—doesn't account for pre-main time (dynamic libraries, runtime). For pre-main: DYLD_PRINT_STATISTICS environment variable in Xcode scheme.
Android:
class App : Application() {
override fun onCreate() {
val start = SystemClock.elapsedRealtime()
super.onCreate()
// ... initialization
val initDuration = SystemClock.elapsedRealtime() - start
FirebaseAnalytics.getInstance(this).logEvent("app_init_duration") {
param("duration_ms", initDuration)
}
}
}
SystemClock.elapsedRealtime() is more accurate than System.currentTimeMillis() for measuring intervals.
Dashboard and Alerts
Minimal metrics to monitor:
| Metric | Tool | Target |
|---|---|---|
| Cold launch P50 | Firebase / MetricKit | < 1.5 sec |
| Cold launch P95 | Firebase / MetricKit | < 3.0 sec |
| Cold launch by versions | Firebase | Not growing |
| Slow cold launches (> 5 sec) | Firebase | < 5% |
Alert on P95 growth between versions—more important than absolute value. 500ms regression between two releases signals to investigate diff.
In Grafana or Firebase Alerts, set notification: if current version's P95 cold launch exceeds previous version's P95 by 20%, notify Slack.
Timeline: one-two workdays for basic Firebase Performance / MetricKit integration plus automatic alerts.







