Speeding up Development Build Times With Conditional dSYM Generation

Are you a user of a 3rd party crash reporting service, like Crashlytics or HockeyApp, who wants to speed up simulator builds? I've got just the right not-so-weird trick for you today.

Theory

There's been a lot of advice about speeding up build times during the past few years. Most authors suggest changing Debug Information Format to DWARF for Debug configuration. These days it's the default in Xcode. With this setting, debug symbols (think: names of classes and methods) are put directly inside the binary.

Alternatively, there's DWARF with dSYM file option. It has two pros over DWARF:

  • the binary is obfuscated
  • the binary has a smaller size

and a big con:

  • it leads to longer build times because a separate dSYM file has to be generated during each build ☹️

That's the default in Release configuration. It makes a total sense in that case.

The Problem

The setup described above is completely fine in many cases. It fails, though, when we start to use an app built with DWARF configuration not attached to the debugger 1. When we do that, we end up with unsymbolicated crash reports, like this one:

0   YourApp                         0x00000001001e1594 0x100058000 + 1611156
1   YourApp                         0x00000001000ed74c 0x100058000 + 612172
2   UIKit                           0x000000018f2b67b0 -[UIApplication sendAction:to:from:forEvent:] + 96
3   UIKit                           0x000000018f42a5ec -[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 168

We have to symbolicate them manually using symbolicatecrash tool, e.g.:

symbolicatecrash YourApp\ \ 17-10-16\ 23-15.crash YourApp.app/YourApp > symbolicated.crash

Note that we need to have an access to the .ipa (or .app) file for the build that crashed. .ipa files aren't created automatically except for the Archive action. This makes symbolication process of those crashes cumbersome. That's why services like Crashlytics encourage us to generate dSYM files on each build:

The default set up process ensures that we get your app’s dSYMs on every build (assuming a code change). This is the preferred configuration, so that if a crash comes while you develop and do internal testing, we can show you the exact line of code that caused the crash.

I thought that it should be possible to symbolicate crashes directly on the device but it doesn't seem so anymore because:

I think that's why third party crash reporters require dSYM files in each case. So, since we now know why dSYM files are needed, let's see how to save those precious build time seconds.

The Trick

It seems like the choice is absolute, we either generate a dSYM file or not. All hope is lost…

It turns out that it's possible to set different Debug Information Format values based on a target SDK!

Different Debug Information Format per SDK

What this setup gives us is:

  • faster simulator builds (for which we almost always have a debugger attached, so crash reports aren't that needed anyway)
  • full crash reports for device builds (which have a higher probability of being run outside of the debugger)

Summary

We cut our simulator build times in a really easy way. In my case, it's a difference of 1.5s (or about 15% of the overall build time) in incremental builds. It doesn't seem like much but each second matters when you try to keep staying in the zone.


  1. Ideally, you'd have a separate version built on your CI for that such use but I'm sure there are cases when you'll use that Debug build and encounter crashes on it (I sure do on my device).