Waiting for launch is the first user experience any app provides. There could be many things happening behind that launch screen of your app and of course, it should be ready for use asap. Fancy animations can only go so far to interest the user, eventually, they just want to get the thing done!
And, since an app can be launched n times in a day, it is important to care about this metric. All the time elapsed between the user tapping your app icon and the first View being drawn on the screen contributes towards the perceived launch time of your app.
Before we talk about the factors that contribute towards the launch time, let’s see the various types of launches.
Types of launch sequences
- Cold Launch — This means that the App process doesn’t exist in the system’s kernel buffer cache. This generally happens when the app is launched for the first time or when the device has been rebooted and the kernel cache has been cleared. This kind of launch takes the maximum time and hence should be considered for launch time analysis.
- Warm Launch — This is the type of launch where the application process and data exist in the system’s memory and is then brought into the foreground. This is similar to killing the app and relaunch.
- Hot Launch — This launch happens when the app is brought to the foreground from the background or suspended state.
Now, that we know about the launch sequences, we need to talk about the two categories of launch times.
If you are an Objective C developer, you might already have seen the main() of your app, but even if you are writing in Swift, this method is still present (of course!!) but hidden from plain sight.
All the time taken until the control reaches your app’s
main() function is counted towards pre-main time.
And, since all this happens before the control reaches your app, this time is difficult to control. Well, not really, but we’ll talk about that.
Dyld has a built-in mechanism to measure this, all you have to do is edit your app scheme and add an environment variable
1 , as can be seen below
You can also set the build configuration to
Release mode and it would be better to run this on an iPhone instead of the simulator. Once the configuration is done and you run the app, you’ll see something like this printed in the console.
There are multiple factors affecting the launch times like dylib loading time, rebase/binding time, etc. and we’ll discuss them in some time.
First, we need to look into the second category of launch time.
Post-main() launch time
This is the time taken from the start your app’s
application:didFinishLaunchingWithOptions of your
AppDelegate all the way up to the first ViewControllers drawing cycle end i.e. until
viewDidLayoutSubviews are called.
As you might have guessed, to measure this time you’ll have to measure the time from
willFinishLaunching method to the
viewDidLayoutSubviews of the first ViewController, which in general is the Walkthrough/Home Screen of your app.
And, we’ll discuss the things affecting this launch time too.
So the final equation for total launch time is
pre-main() time + post-main() time = Total launch time of App
Now, let’s talk about the most anticipated part of this article
Factors that affect the launch time performance
There are several factors that affect your launch time performance
Dynamic Library Load Time
The dynamic loader (
dyld) loads the app’s executable file and examines the load commands in the executable to find frameworks and dynamic libraries that the app needs. Any dynamic framework added to your app contributes towards this time.
Hence, limiting the number of frameworks you embed in the “Linked Frameworks and Libraries” setting of Xcode can help in reducing launch time.
You can read more about the dyld here.
Another solution to this is to use static frameworks, they are built into the app executable at compile-time, hence dylib doesn’t have to do all the above work.
By just doing this, you can save a ton of pre-main() launch time.
Although, with iOS 13, dyld3 is included in iOS apps and this is a big improvement as this will make the iOS apps launch 2x faster. But, it is still no match for using static frameworks.
Certain code in your app must run before iOS run your app’s
main() function, adding to the launch time. This code includes
- C/C++ Static Initializers — Normally, you wouldn’t find something like this in your app, but if you do, I’d love to know more about it. Any function defined as
__attribute__((constructor))is a static constructor in C, and their specialty is that they get called before your main() is executed! You can read more about it here.
- Objective C
+loadmethods defined in classes or categories. They are called before your main() and increases the ObjC setup time as you can see in the console image above. Also, this method is deprecated, you should use
- Any function linked to the
__DATA,__mod_init_funcsegment of an app executable or Mach-O. These functions are usually located inside the __DATA segment’s __mod_init_func section.
So, as a general rule of thumb, move the code to a later stage of the app’s life cycle, after the app has finished launching but before the results of the work are needed.
You can use the Static initializer instrument in Xcode to find out how much time does your app spends running static initializers.
If you would like to read more about the process that happens before your
main() executes, please read this article.
UIKit Lifecycle Methods
These methods are called on the main thread and since the time spent to execute them is counted towards launch time, it is advisable to only create the app’s initial display here and defer other tasks.
If it makes sense to show stale data to the user, defer data model synchronization or API calls until the app is running. Initialize non-view functionality, such as persistent storage and location services, on first use rather than on app launch.
Drawing Initial View Hierarchy
The time required to draw the views that are required on the first frame of the app is also counted towards the launch time. In UIKit every view is added to the view hierarchy on the main thread and thus more complicated view hierarchy leads to higher launch time.
Reducing the complexity of the view hierarchy and replacing the views that override
draw method with regular UIView will help in improving the load time.
Try rendering only the parts of view that are visible on the screen.
There could be many other reasons which can contribute to the launch time, but these are the main ones I could find, and, I believe if you religiously follow the above approaches, you’ll create a better experience for your users.
- There are three types of launch sequences viz. Cold, Warm and Hot.
- Launch time is divided into two categories:
Pre-main — Time taken before
Post-main — Time taken after
main()execution till the first ViewController is visible.
- We looked at several factors that contribute to the increased launch times: dynamic library loading time, static initializers, the time taken to execute AppDelegate lifecycle methods and the time taken to draw the first ViewController.
- We found out that you should really read this article to know more about the pre-main() process. 😛
Where To Go From Here
If you are interested in videos, you can check out these amazing WWDC videos.
Optimizing App Startup Time - WWDC 2016 - Videos - Apple Developer
Launching an App is a complicated and subtle process and the ramifications on launch times of different App design…
Optimizing App Launch - WWDC 2019 - Videos - Apple Developer
Slow app launches are frustrating. Learn about the new app launch instrument and discover how to make your app launch…
Or, you can read more about the launch time here