Testing your app launch time (iOS)

Morgan Collino
Level Up Coding
Published in
7 min readApr 1, 2020

--

App launch is the time from when the user tap your app icon, to the first screen interaction-able by the user of your app.

The longer it takes to launch your app, the worse your user retention and app usage can deteriorate. If your app takes a few seconds to launch, the users might just switch to a different app because it doesn’t want to wait for it.

That’s why the quicker the app launch is, the better it is for you and your users. Apple recommends the launch time to be around ~400ms.

Easy to target this constraint when you have a small project. Another deal when you have a large project (getting bigger and bigger). Adding framework dependencies and business logic, this will eventually be a problem.

Quick definitions

Before diving into how to test, there is some learning to do. First of all, there are two types of launch:

  • Cold: Kill the app, relaunch the iOS device (let it cool after relaunching for a couple of minutes), then launch the app.
  • Warm: Kill the app, relaunch the app.

The most important one is the cold launch, since the first impression from your user with your app will be a cold launch and also is the one which takes the most time.

https://developer.apple.com/videos/play/wwdc2019/423/

Launch Time calculation

Launch time is calculated with two key metrics:

  • Pre-main time: Time before the first function (main) of your app is executed. This time is reserved to the systems binding resources needed by your app to launch properly.
  • Post-main time: Time when your app is taking the control from UIApplicationMain (or main function) to the first screen interact-able presented to your user. In simpler words, it’s how long it takes you to render the first frame starting from the AppDelegate / Main.

Launch time is the addition of those two times.

What could cause the launch time to be slow?

There could be multiple reasons but the common ones are:

  • Dynamic linking (dyld)
  • Business logic done before showing the first screen interact-able

What’s the matter with dynamic linking?

Having many frameworks linked as dynamically makes the OS system to do many bindings, which then increase the pre-main time.

If you are using Cocoapods / Carthage, you might have a consequent amount of dependencies, with then might even also trigger transitive dependencies (Basically dependencies including their nested dependencies etc.)

This could accumulate a lot of linking at launch and could then impact your launch time.

How do I know the numbers of dynamic frameworks linked to my app?

To see the number of dynamic dependencies your app has, add the environment variables DYLD_PRINT_LIBRARIES=1 to your run app scheme.

This will print out to the console the frameworks loaded at launch time.

Then to count them out, you will have to make a search within the launch console output with something looking like this:

dyld: loaded <UUID> /private/var/containers/Bundle/Application/<UUID>/<appName>.app/Frameworks/<FrameworkName>.framework/<FrameworkName>.

Count each lines printed like above to know the count of your app dynamic dependencies.

It is not necessary to count Apple’s dynamic framework loading.
Apple recommendation is to have maximum 6 dynamic dependencies. (yikes)
Also, if your app takes more than 20 seconds, the OS will automatically kill the app (Watchdog), this only really concerns the oldest devices usually, so watch out.

All those parameters counts in the launch time assessment:

  • Device type: The older the device, the longer it will take to load libraries.
  • OS version: iOS 13 did some optimization with caching dynamic dependencies for apps, which significantly improves the warm launch, prior versions of iOS don’t benefit this improvement.
  • Number of dynamic dependencies: The more you have, the longer your launch time will be.
  • What you do before making the first screen visible: If you are loading external dependencies (eg: Analytics frameworks), initializing data layers (eg: CoreData), this will take time before your user could see the first screen and may impact the launch.

Let’s dive into ways to get metrics of your launch time with Xcode 11+ and iOS 13

Xcode provides Instruments which supports various tools for memory profiling, cpu profiling, time profiling, network profiling, etc. Profiling will help you to find the issues in your app, including the location and details of the issue so you can fix them.

With Xcode 11, Instruments comes up with a new template named App Launch to gather metrics of your app launch.

Before launching Instruments, on thing you need to do is to Profile your app to a physical device. Profile is compiling the app on the release mode and take advantages of compiler time optimization.

Here are the steps on how to gather metrics about your app launch via Instruments.

  1. Open Instruments (Either go through Xcode > Open developer tools > Instruments or via the search option.
  1. Select the device you and the app you want to profile. You definitely need to use a physical device to have an accurate output.
  2. Select App Launch and tap on Choose
  3. Tap on the record button

Now we have the metrics of the launch, let’s display it. On the left panel, tap on the Process chevron and select Time Profile.

This will display the metrics of pre-main and post-main to the interface like this below:

The purple side are phases happening before the main function (pre-main). If this side is big, it means you have more likely a problem with dynamic dependencies.

The green side is post-main function. When the first time you are executing code from your app. The first functions are willLaunchWithOptions and didLaunchWithOptions. You have to minimize the work there to show the first frame as soon as possible. If this part is huge, it more likely means you are doing too much work before handling your first frame.

Remember: Launch time = pre-main + post-main

You could eventually go deeper and look down on the process and which code is either blocking the main thread or taking time executing.

Other ways to get metrics about launch time

Prior to Xcode 11, there we no ways to easily get metrics about launch time. I will explain below few ways to get metrics without using Instruments.

Environment variables

You can pass those variables to print out launch time metrics. To add those go your to your app scheme > edit > Run > Environment variables > +

Environment variables

DYLD_PRINT_STATISTICS will output something looking similar to this:

This show the details of pre-launch time of the device. We can see here that the dylib is taking huge chunk of the pre-main time.

Manual QA

Another way to gather metrics about launch time is to QA manually the launch time. Profile your app to a physical device and test it manually.

To do this, few instructions could serve you up:

  1. Use StopWatch from Google
  2. Test Cold and Warm launch:
  3. Cold: Reboot your device, wait couple of minutes after reboot. The proceed to the next step.
  4. Warm: Kill the app, then proceed to the next step
  5. At the same time of tapping the icon of your app, tap on the start button of the StopWatch
  6. When you see the first screen showing (after the LaunchScreen of course), stop the counter.

Reproduce this test with multiple devices and OS. Especially old devices and iOS 12 or previous.

It will give you the overall launch time. To differentiate pre-main and post-main, I suggest to add logs to your code and output to the console when the first function is triggered (usually: willFinishLaunchingWithOptions) and when the first screen viewDidAppear is fired.

This will get you the time for the post-main and you will just have to reduce the time to your overall launch time.

Add metrics collection in your code

You can add code reporting metrics to a custom endpoint. You can easily add a timer starting before executing a block of code and reporting the time elapsed when it finish the work from the block.

For example, you want to measure how long the database takes to initialize, call this measure func with the block containing the database initialization code and this will output the results.

source

Firebase Performance is also a great tool (and another dependency) to add to your project to collect performance metrics about your app and also monitoring it. You can create traces with the tool to time measure a block of code.

Example projects

You can find the example projects in this Github repository including an empty project with various numbers of dependencies with their Instruments AppLaunch traces.

Thanks for reading this blog, I am sure you may have additional pointers/questions from your own experience. Feel free to share them by leaving comments, tweet at @morgancollino or linkedIn.

--

--