With iOS 13, Apple has released
histogrammedTimeToFirstDraw, which has a timer that “starts when the user taps the app icon and ends when the system draws the first screen that’s not the launch screen”. Try seeing if that meets your needs before going through this article.
Before an app even runs
+applicationDidFinishLaunching, a considerable amount of work is done, including setting up dylibs for use, running
+load methods, and more. This can take 500ms or more. Blog posts like this one show you how to measure it with the debugger, using
DYLD_PRINT_STATISTICS, but it’s hard to find any help for measurement in the wild.
How To Do It
You can get the process start time with the following code:
The process start time that we get from
processStartTime is given in seconds since 1970. Unfortunately, this is not so great for timing because it can be changed, e.g. if the phone synchronizes its time with a server in between when the process starts and when we run
[NSDate date].timeIntervalSince1970 in
main(). This would be a very rare case, but it should be kept in mind (you may want to remove negative and ridiculously large values from your results).
Main thread CPU Time
You can also get the amount of CPU time the main thread has spent by running this code on the main thread:
This number represents the amount of time the main thread has spent doing work, but does not include the amount of time it was waiting, e.g. due to being preempted by another process or thread. In practice, it will be about the same as the time taken from the method in the gist (I measured it being consistently 7ms less). This number may be preferable if you don’t want to time anything that’s out of your control.
The pre-main() time provides an important piece of information in understanding your overall launch time. By measuring from the very start of the process to when the app is actually interactive, you can gain insight into the actual experience for your users.