Skill: Startup (Launch) Optimization
Overview
Launch time is a first-impression metric Apple tracks (and surfaces in MetricKit/Xcode Organizer). It splits into pre-main (dynamic linking, static initializers) and post-main (your didFinishLaunching/first-frame work). The goal is a fast time to first usable frame: defer everything non-essential, do no blocking I/O or network on the launch path, and measure with the App Launch Instrument. Target well under ~400ms to first frame where possible.
Use Cases
- Slow cold launches hurting retention and App Store metrics.
- Heavy SDK initialization bloating startup.
- "White screen" before first content appears.
Best Practices
- Defer non-critical work off the launch path (
Taskafter first frame, lazy init). - No synchronous network/disk in
didFinishLaunchingor first viewinit. - Lazy-initialize SDKs/analytics; initialize only what the first screen needs.
- Reduce pre-main cost: fewer dynamic frameworks, avoid heavy
+load/static initializers. - Measure with the App Launch Instrument and MetricKit; set a launch budget.
Anti-Patterns
- ❌ Initializing every SDK eagerly at launch.
- ❌ Synchronous network/database calls before first frame.
- ❌ Heavy work in the root view's
init/onAppear. - ❌ Many dynamic frameworks inflating pre-main linking.
- ❌ Optimizing launch without measuring it.
Checklist
- [ ] No synchronous network/disk on the launch path.
- [ ] Non-critical work deferred past first frame.
- [ ] SDKs lazily initialized; only first-screen deps loaded eagerly.
- [ ] Pre-main cost considered (frameworks, static init).
- [ ] Launch measured with a budget.
Swift Examples
swift
@main
struct BankingApp: App {
init() {
// Keep launch lean: only what the first frame needs.
CrashReporter.startMinimal()
}
var body: some Scene {
WindowGroup {
RootView()
.task {
// Deferred, non-blocking startup work after first frame.
await AppBootstrap.shared.warmCaches()
Analytics.shared.initializeLazily()
await RemoteConfig.shared.refresh()
}
}
}
}
enum AppBootstrap {
static let shared = AppBootstrap()
func warmCaches() async { /* prefetch off the critical path */ }
}Common Interview Questions
- What are pre-main vs post-main launch phases?
- How do you measure launch time?
- Why avoid eager SDK initialization at launch?
- How do dynamic frameworks affect launch?
- What is "time to first frame" and why does it matter?
AI Implementation Notes
- Move startup work into a deferred
.task/background after first frame; never block launch. - Lazily initialize SDKs and analytics.
- Recommend measuring with the App Launch Instrument and setting a budget.
- Related:
memory_optimization.md,battery_optimization.md.