How did Apple cut launch time by 30% in iOS 27?
An Instruments-flavoured optimisation deep-dive.
Launch time is the most impactful metric you never optimise.
Say you’re WhatsApp.
You have a kajillion daily active users, except in America for some reason.
They may be warm-launching your app 10x a day. More.
A launch speed of 0.5 seconds, times 10, times a kajillion, means even a 1% improvement probably saves a few lifetimes’ worth. I haven’t checked my maths, and I’m not going to.
Apple, helpfully, checked theirs. At WWDC19’s Optimizing App Launch session, they computed across billions of daily launches: shaving just 1ms off launch saves just about enough time each day to send a rocket to Mars.
Apple had a “Snow Leopard” year this WWDC. Less flashy features, less UI overhauls nobody asked for, more cold platform optimisations. They claimed to have added a 30% boost to app launch times in iOS 27.
I’ve chatted to you before about optimising launch time on a legacy app, and tracing launch performance in production. What I haven’t done is go all the way down. So today that’s what we’ll do:
We’ll get to grips with the important stuff for devs to know about launch time.
We’ll trace iOS 26 and 27 improvements using Instruments.
We’ll clarify what Apple has actually done to improve things.
Launch Theory
Flavours of Launch
There are three kinds of launch:
🐢 A cold launch is what you think of as a “launch”. The app process doesn’t exist, your binary is sitting in storage. WWDC2016 explains that cold launches mostly only happen the first launch after install or restart. This is your first impression, though, so you probably want to worry about this.
🐇 A warm launch spawns a fresh process, but iOS caches binary memory pages aggressively in the kernel file cache, and dyld still holds onto a precomputed launch plan. tl;dr: the data is closer to where it needs to be.
🐅 There is no “hot launch”. It’s just a resume, foregrounding a backgrounded or suspended app that is already alive in process memory.
Launch Phases
There are 2 phases of launch to understand, based on when the main() function is called in your app process.
Knowing about pre-main launch steps is a great way to get ahead when your job interviewer is a jerk. First the kernel maps your Mach-O executable into process memory. Dyld, the dynamic linker, loads up the dynamic frameworks needed by your app, including your own code, third-party dependencies, and system frameworks like Foundation, UIKit, and Swift.
Address Space Layout Randomisation (ASLR) is a critical protection against memory corruption exploits, randomising the memory address of many of these frameworks in process memory at launch. Therefore dyld applies “fixups” to write symbol references to real memory addresses.
The Swift runtime sets up type metadata, protocol conformances, and generics. Finally, some of your own code might run: static initialisers and Objective-C load() methods.
This is a ton of work, but we will shortly see it visible in our instruments traces.
Nobody’s giving you a job for knowing about post-main. If you’re into CLIs, or hardcore (like me), you might have your own explicit main() method.
// main.swift
import UIKit
UIApplicationMain(
CommandLine.argc,
CommandLine.unsafeArgv,
nil,
NSStringFromClass(AppDelegate.self)
)UIApplicationMain creates the app object, sets up the run loop, the app delegate, and ultimately calls application(_:willFinishLaunchingWithOptions:). You know the rest, and if you don’t, my blog has a lot of solid iOS content.
Launch ends when your first frame renders. Optimising this side is standard engineering: profile, parallelise, and minimise the amount of work on the critical path before the user gets what they’re looking for.
Reading the Launch Trace
The App Launch instrument gives a cool visual overview of the theory above.
Launch Executable covers the full pre-main process.
Validating Closure at the start is the tell for a warm launch, loading the launch plan computed by dyld. In cold launches, it’s Building Closure, lives at the end, and takes much longer.
Map Image takes the actual file paths of your dynamic frameworks and mmaps them into your app process. This is where an overabundance of dynamic frameworks can slow down your launch.
Apply Fixups wires these symbol references to actual memory addresses.
ObjC Image Init makes classes visible to the Objective-C runtime.
Run Static Initializer… runs your static initialisers.
dlopen handles library loads at runtime. You can call this yourself!
Post-main, we can also see UIKit initialisation, didFinishLaunchingWithOptions, UIScene creation, then the declaration of victory: Foreground — Active*.
*god I miss being allowed to use the em-dash
Xcode 27 adds a cool flamegraph.
On the Time Profile, hit the little blue button in the top-right, filter by your process, filter to the main thread, and you’ll see something like this:
The % gives each segment’s share of CPU time in the given time window. To make things more useful for us, we can hide system libraries and see how our own code breaks down at launch*.
*The flamegraph is also useful for non-launch performance work.
I’m not throwing away my shot 🎯
I’m not Apple. I don’t have a bargain bin full of iPhone 11 Pro Maxes to profile with. I get one shot per year to run a proper before-and-after to profile OS upgrades.
Since I don’t want the results to be too good, I whipped out my trusty iPhone 13 mini (from my cable drawer, which is inexplicably strewn across my desk).
Fortunately, I realised this before the automatic beta update landed. Just.
I vibe-coded a deliberately annoying Frankenstein’s monster of dynamic frameworks, static initialisers, and protocol conformances by pulling together a bunch of demo apps from previous articles. I figured I’d get more interesting results by attempting to give indigestion to dyld.
OK. This is the bit you’ve been waiting for.
Let’s take a look at how iOS 27 changes things.
iOS 26 vs iOS 27
Because I’m very stupid, I only took 3 traces on iOS 26 (2 warm; 1 cold). Then, contritely, 2 warm and 2 cold for iOS 27. Look, I’m not a proper scientist, and you mostly read this for the feel-good vibes. Profile it yourself if you have to.
Seriously, the codebase (and traces) are open-sourced here:
Cold Launch (iOS 26)
1,090ms
pre-main ~550ms
post-main ~540ms
Warm launch (iOS 26)
582ms
pre-main ~145ms
post-main ~437ms
Immediately you can see the timescale of pre-main time is drastically squashed. Almost 4x faster, with the closure and memory mapping steps drastically quicker. The blue fixups step seems about the same, which checks out because ASLR is reapplied at launch.
Ok. Let’s do this.
*Irreversibly installs iOS 27*
Cold launch (iOS 27)
862ms
pre-main ~470ms
post-main ~390ms
Warm launch (iOS 27)
449ms
pre-main ~67ms
post-main ~382ms
On our only mildly scientific test, we’ve successfully validated a (non-statistically-significant) speedup:
The cold launches were 21% faster.
The warm launches were 23% faster.
Most of the drop comes from the pre-main phase. Closure rebuilding was 1.5x faster, fixups were 2.5x faster, and static initialisers were 3x faster. The remainder mostly consists of I/O work, such as memory-mapping for all the dynamic binaries (orange), which seem about the same on both OS versions.
The shape of the dyld phase remains the same across each sequence. This isn’t the full rebuild of dyld cited in previous OS releases. But our data checks out: Apple claimed their 30% speedup came from 2 things:
“An optimised CPU scheduler”, which I suspect is just a Staff Marketing Exec’s way of saying “better parallelisation of dyld work”.
“Preloading key app data”, which probably refers to the faster closure rebuilding and static initialisers we measured, speculatively.
Post-main had a bit of a boost to Initial Frame Rendering, where Core Animation commits the first frame of the UI. Most of the rest of my “launch steps” in the sample project are dumb sleep()s designed to simulate a poorly-thought-out launch sequence.
Apple’s launch improvements, every year
Apple has cited lots of improvements to their launch speeds in the last decade-odd.
2017: dyld3, a full dynamic-linker rewrite dogfooded internally for first-party apps. Mach-O parsing and symbol resolution were moved out-of-process, cached as launch closures.
2018: apps launch “up to 2× faster” while multitasking a lot by warming up CPU clock speed when it detects you’re scrolling the Home Screen, about to launch an app.
2019: apps “could launch up to 2× faster” as Apple rolled out dyld3 to third-party apps.
2022: some apps “launch almost 2× faster” due to improvements in the dynamic linker like pre-computed protocol conformances and page-in linking that handled fixups lazily on memory access rather than at launch.
2026: apps launch “up to 30% faster” via an optimised CPU scheduler and preloading key app data. I’m not going over this again. I also found this interesting decompilation deep-dive by CodeColorist that identifies potential dyld cache optimisations.
Hmm…
2x… 2x… 2x… and 1.3x… this multiplies up to a potential 10.4x improvement to launch time for “some apps”.
Wait a minute.
Since 2016, we’ve jumped from A9 to A19 chips too. Another 10x increase.
So, uh, why is app launch not ~100x faster than 2016?
I reckon we need to do less sh*te at launch. Let’s switch off some of those marketing SDKs 👀
Last Orders 🍺
Every couple of years, Apple likes to take their system guys out of hibernation to tweak the dynamic linker, CPU scheduling, or pre-warming in order to eke another 2x, or perhaps a diminishing 1.3x, or more likely my measured 1.2x, performance boost out of their launch times.
This is nice. But the onus is still onto you to architect your app’s critical launch path to be responsive to your users.
Profile your launch path with the App Launch instrument and a nifty flamegraph.
Optimise pre-main with thoughtful use of static, dynamic, & mergeable libraries.
Minimise the work on your critical post-main launch path.
Render your initial UI as quickly as possible, only showing what the user needs.
That’s the iOS 27 launch for now. See you next year 👋
If you enjoyed my post, subscribe free to join 100,000 senior Swift devs learning advanced concurrency, SwiftUI, and iOS performance for 10 minutes a week.
Paid members get much more:
⚓️ Access my full library of 50 paywalled articles
🚀 Read free articles a month before anyone else
🧵 Master concurrency with my full course and advanced training
🧑🚀 Get a free copy of my new eBook, “Land your iOS Tech Job”
❤️🩹 Support independent, sometimes funny, tech writing
















