DEV Community

Sebastien Lato
Sebastien Lato

Posted on

SwiftUI App Startup Time Optimization (Cold Launch, Warm Launch, Perceived Speed)

Users judge your app before they ever see a screen.

If startup feels slow:

  • they assume the app is buggy
  • they abandon early
  • they never trust performance again

SwiftUI makes it easy to accidentally slow startup:

  • heavy App initialization
  • eager dependency creation
  • blocking work on launch
  • synchronous disk access
  • analytics & config boot storms

This post shows how to design fast, predictable, production-grade startup architecture in SwiftUI — both actual speed and perceived speed.


🧠 The Core Principle

Startup is a pipeline, not a moment.

There is no “app launch”.
There is:

  • cold launch
  • warm launch
  • foreground resume
  • perceived readiness

Each must be optimized separately.


🚀 1. What Actually Happens at Launch

Simplified pipeline:

Process start
→ App init
→ Scene creation
→ RootView init
→ body evaluation
→ initial render

Anything blocking before first render hurts startup.


🧱 2. The #1 Startup Killer: Eager Initialization

Bad:

@main
struct MyApp: App {
    let api = APIClient()
    let analytics = Analytics()
    let cache = Cache()
    let database = Database()

    var body: some Scene {
        WindowGroup {
            RootView()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Everything runs before first frame.


✅ Correct Pattern: Lazy Initialization

final class AppContainer {
    lazy var api = APIClient()
    lazy var analytics = Analytics()
    lazy var cache = Cache()
    lazy var database = Database()
}
Enter fullscreen mode Exit fullscreen mode
@main
struct MyApp: App {
    let container = AppContainer()

    var body: some Scene {
        WindowGroup {
            RootView()
                .environment(\.container, container)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Nothing initializes unless used.


⏳ 3. Defer Non-Critical Work

Never block launch for:

  • analytics
  • feature flags refresh
  • cache cleanup
  • background sync
  • migrations (unless required)

Pattern:

.task {
    await appStartupTasks()
}
Enter fullscreen mode Exit fullscreen mode
func appStartupTasks() async {
    async let flags = featureFlags.refresh()
    async let analytics = analytics.start()
    _ = await (flags, analytics)
}
Enter fullscreen mode Exit fullscreen mode

First frame appears immediately.


🧠 4. Cold vs Warm Launch

Cold Launch

  • app not in memory
  • most expensive
  • optimize aggressively

Warm Launch

  • app suspended
  • fast resume
  • avoid heavy onAppear

Never assume launch == cold.


📦 5. Avoid Disk I/O at Startup

Bad:

let data = try Data(contentsOf: url)
Enter fullscreen mode Exit fullscreen mode

at launch.

Instead:

  • load cached metadata only
  • defer heavy reads
  • stream when needed

Disk access is slow and unpredictable at startup.


🧬 6. AppState Should Be Lightweight

Bad AppState:

class AppState {
    let user = User()
    let feed = FeedViewModel()
    let settings = SettingsViewModel()
}
Enter fullscreen mode Exit fullscreen mode

This loads everything on launch.


✅ Correct AppState

class AppState {
    @Published var session: Session?
    @Published var launchPhase: LaunchPhase = .loading
}
Enter fullscreen mode Exit fullscreen mode

Features load on demand.


🧭 7. Perceived Performance Matters More Than Real Performance

Users don’t measure milliseconds.
They measure feedback.

Show something immediately:

  • splash placeholder
  • skeleton view
  • brand screen
  • cached content

Even 200ms of silence feels broken.


🧠 8. Startup State Machine

Model launch explicitly:

enum LaunchPhase {
    case loading
    case authenticated
    case unauthenticated
    case error
}
Enter fullscreen mode Exit fullscreen mode
switch phase {
case .loading:
    LaunchView()
case .authenticated:
    MainApp()
case .unauthenticated:
    Login()
case .error:
    Recovery()
}
Enter fullscreen mode Exit fullscreen mode

No guessing. No race conditions.


🧪 9. Measuring Startup Time

Use Instruments:

  • Time Profiler
  • App Launch template

Measure:

  • time to first frame
  • time to interactive
  • main thread blocking

Never rely on “feels fast”.


⚠️ 10. Common Startup Anti-Patterns

Avoid:

  • network calls in init
  • database migrations on launch
  • synchronous JSON decoding
  • loading all features =- global singletons doing work
  • blocking @main

These destroy startup.


🧠 Mental Model

Think:

Launch
  Show UI immediately
    Load minimum state
      Defer everything else
Enter fullscreen mode Exit fullscreen mode

Startup is progressive, not atomic.


🚀 Final Thoughts

A fast startup gives you:

  • better retention
  • better reviews
  • higher trust
  • smoother onboarding
  • fewer early crashes

SwiftUI doesn’t make startup slow.
Architecture does.

Once startup is clean:

  • everything else feels faster
  • performance issues are easier to isolate
  • your app feels professional

Top comments (0)