| name | cold-start-optimization |
| description | Reduce cold start time (TTID and TTFD) on iOS and Android using baseline profiles, main-thread hygiene, and selective pre-warming. Use when launch latency exceeds the budget in Agent.md. |
Cold Start Optimization
Instructions
Optimize cold start — the scenario where the OS creates a new process and your app draws its first meaningful frame. Cold start is what app-store reviewers and lab devices measure, so it is the number that matters for reviews and rankings.
1. Define the Target Metrics
Two numbers matter:
- TTID (Time To Initial Display) — first frame drawn. On Android this is
Displayed in logcat. On iOS it is the moment UIWindow commits its first frame.
- TTFD (Time To Full Display) — first useful frame with real data. On Android call
Activity.reportFullyDrawn(). On iOS emit an os_signpost event named TTFD.
Targets (p50 device, release build):
| Metric | iOS budget | Android budget |
|---|
| TTID | 1.2 s | 1.5 s |
| TTFD | 2.0 s | 2.5 s |
2. Baseline First
Before changing code, capture a baseline.
Android (Macrobenchmark):
@RunWith(AndroidJUnit4::class)
class StartupBenchmark {
@get:Rule val rule = MacrobenchmarkRule()
@Test fun coldStartup() = rule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 10,
startupMode = StartupMode.COLD,
) { pressHome(); startActivityAndWait() }
}
iOS (XCTest):
func testLaunchPerformance() {
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
3. Make the Main Thread Boring
Every millisecond before the first frame is paid on the main thread. Audit and remove:
- Disk IO in
Application.onCreate / UIApplicationDelegate.application(_:didFinishLaunchingWithOptions:).
- Synchronous network calls, feature-flag fetches, remote-config SDK init.
- Eager DI container construction (Hilt
@EntryPoint, Swinject assembler, get_it registrations) for features not on the start screen.
- JSON parsing of cached payloads. Defer to
Dispatchers.IO / a background DispatchQueue.
- Heavy reflection or annotation scanning (Jackson, Gson with
@SerializedName at startup).
Android pattern — use Initializer from androidx.startup and disable default init for expensive libs:
class AnalyticsInitializer : Initializer<Analytics> {
override fun create(ctx: Context): Analytics = Analytics.init(ctx)
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
iOS pattern — replace +[load] with +initialize or lazy static let. Never do work in +[load].
enum FeatureFlags {
static let shared = FeatureFlagsClient()
}
4. Ship a Baseline Profile (Android)
Baseline Profiles AOT-compile the hot startup path and typically reduce cold start by 15–30%.
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val rule = BaselineProfileRule()
@Test fun generate() = rule.collect(packageName = "com.example.app") {
pressHome(); startActivityAndWait()
device.findObject(By.res("feed")).scroll(Direction.DOWN, 0.8f)
}
}
Commit the produced baseline-prof.txt into src/main/ and add androidx.profileinstaller.
5. Pre-warm Only What the First Frame Needs
- iOS: prebuild the root
UIView / SwiftUI View tree off the critical path using Task.detached for side data only — never for the first frame's view tree.
- Android: in
Application.onCreate, post non-critical work with Handler(Looper.getMainLooper()).post {} so it runs after first frame.
- Flutter: use
SchedulerBinding.instance.addPostFrameCallback to defer work until after the first frame; mark shell route with HeroMode(enabled: false) during launch.
- React Native: enable Hermes; move bridge-heavy native modules to lazy registration via
TurboModule on the New Architecture.
6. Splash Screen, Not Blank Window
Use the OS-provided splash APIs — SplashScreen on Android 12+, LaunchScreen.storyboard / .xcassets on iOS. A themed splash hides the process-creation window and improves perceived TTID without adding real time.
7. Measure Again, Record in CI
Add the benchmark to CI. Fail the build on > 5% regression vs a committed baseline JSON. See startup-profiling skill.
Checklist