Skip to Content
Course content

150: Reducing App Launch Time

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're opening a physical coffee shop for the morning. You have the keys, you unlock the front door, and you're ready to go. But instead of letting the first customer in, you decide that before anyone can enter, you must personally hand-grind every single bean for the day, polish every window, scrub the floors, and organize the napkins by color. The customer is standing on the sidewalk, staring at the closed door, wondering if the shop is even open.

That’s exactly what happens when we overload our app's launch sequence. In Swift terms, the "closed door" is the splash screen. The "hand-grinding beans" is all that heavy initialization code we cram into didFinishLaunchingWithOptions. The user doesn't care that your database is perfectly indexed; they just want to see the UI.

The "Everything Everywhere All At Once" Problem

I've seen this a dozen times in professional codebases: a AppDelegate that looks like a shopping list of every third-party SDK in existence. You've got Firebase initializing, a database migration running, a logging framework starting up, and maybe a complex theme manager loading JSON from the disk—all happening synchronously on the main thread.

When the system calls didFinishLaunchingWithOptions, it's essentially a timer. If you take too long (usually a few seconds), the watchdog timer kills your app. But even if you don't hit that limit, a three-second delay feels like an eternity to a user. They'll think the app is frozen and just swipe it away.

Stop Blocking the Front Door

The secret to a snappy launch is knowing what actually needs to happen before the first frame renders. Does your Analytics engine need to be fully configured before the user sees the Home screen? Probably not. Does your local cache need to be fully warmed up? Definitely not.

Instead of doing everything upfront, move the non-critical path to a background queue. I usually wrap these in a dedicated StartupService. Here is the difference in approach:

// The "Slow Shop" approach
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Database.shared.heavyMigration() // Blocks main thread
    Analytics.shared.setup()        // Blocks main thread
    ThemeManager.shared.load()      // Blocks main thread
    return true
}

// The "Efficient Shop" approach
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Only do the absolute bare minimum here
    
    Task(priority: .background) {
        await Database.shared.heavyMigration()
        Analytics.shared.setup()
        ThemeManager.shared.load()
    }
    
    return true
}

By moving these to a background task, you're letting the customer into the shop immediately. The coffee is still being brewed in the back, but the user is already interacting with the menu.

Lazy Loading is Your Secret Weapon

Beyond just moving things to the background, you should question if some objects need to exist at all during launch. We often create singletons or manager classes that we might use, but not until the user navigates three screens deep.

If you have a PaymentProcessor or a PDFGenerator, don't initialize them in the app delegate. Use lazy properties or a factory pattern. I'm a big fan of the "Initialize on Demand" strategy. Instead of a global shared instance that loads everything in its init, make the setup method explicit or the property lazy.

Remember: every millisecond you shave off the launch time reduces your "churn rate." Users are ruthless. If it doesn't feel instant, it feels broken.




📋 Practical Task

Optimize the "HeavyAssetManager" Launch Sequence

You have been handed a project called PhotoVault. Currently, the app takes nearly 4 seconds to launch because the AppDelegate is performing several synchronous, heavy operations: loading a large JSON configuration file, initializing a local SQLite database, and pre-fetching a set of default images from the bundle.

Your Goal: Refactor the AppDelegate to ensure the app reaches the first screen in under 500ms.

  • Identify the three heavy calls in didFinishLaunchingWithOptions.
  • Move the Database and Analytics initialization to a background priority task.
  • Convert the AssetCache from a standard property to a lazy property, ensuring it only loads when the first image is actually requested by the UI.
  • Verify that the return true statement is reached immediately without waiting for the background tasks to complete.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.