Optimizing- swift-build-times

Author: Fastred

Welcome to thumbs up comments, feel useful friends can pay attention to the author’s public number iOS growth refers to north, continue to update

This article is an update of the original, adding the author’s experience and judgment part, if there are mistakes, welcome to correct

preface

After reading How Uber Deals with Large iOS App Size[1], I learned about the optimization of the -osize tag. When searching on GitHub, Find a Optimizing Optimizing point of Optimizing Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point of Optimizing Optimizing point

Note that this optimization is mainly for the Swift part

Incremental compilation mode

Prior to Xcode 10, a common solution to speed up Debug builds was to enable Whole Module optimization.

Currently, it is recommended to set the Compilation Mode to Incremental for Debug builds and Whole Module for Release builds. Also, the Optimization Level under the Debug build should be set to No Optimization.

Type checking of functions and expressions

Typically, Swift builds slowly, mainly because of type checking. By default, Xcode does not display code that compiles slowly. You can find slow-building functions and expressions by adding the following Settings:

  • -Xfrontend -warn-long-function-bodies=100 (100This means 100 milliseconds, and you need to be more precise depending on the actual project and device)
  • -Xfrontend -warn-long-expression-type-checking=100

Set Other Swift Flags in build Settings:

Rebuild and you should now see warnings like the following:

The next step is to fix the code that has problems during compilation. You can find a solution in Resources [3][4][5][6].

Files that compile slowly

In addition to those slow-building functions and expressions, it would be interesting to know the compile time of the entire file. Xcode doesn’t have visualizations and Settings, so you have to create the project in CLI and set the correct flags:

xcodebuild -destination 'platform=iOS Simulator,name=iPhone 8' \
  -sdk iphonesimulator -project YourProject.xcodeproj \
  -scheme YourScheme -configuration Debug \
  clean build \
  OTHER_SWIFT_FLAGS="-driver-time-compilation \ -Xfrontend -debug-time-function-bodies \ -Xfrontend -debug-time-compilation" | \
tee profile.log
Copy the code

If your build project is a workspace, you can replace -project yourproject.xcodeProj with -workspace yourproject.xcworkspace

Please note that this article was written in 2018-2019. Some configuration items are old. Please modify them accordingly

The statistics are then extracted using the following methods:

awk '/Driver Compilation Time/,/Total$/ { print }' profile.log | \
  grep compile | \
  cut -c 55- | \
  sed -e 's/^ *//; s/ (.*%) compile / /; s/ [^ ]*Bridging-Header.h$//' | \
  sed -e "s|$(pwd)/ | |" | \
  sort -rn | \
  tee slowest.log
Copy the code

Finally, you get a grammar. Log file that contains a list of all the files in your project and their compile times. Such as:

2.7288 (0.3%) {compile: account. o <= account.swift} 2.7221 (0.3%) {compile: Messagetag. o <= messagetag. swift} 2.7089 (0.3%) {compile: Edgeshadowlayer.o <= edgeshaDowlayer.swift} 2.4605 (0.3%) {compile: SlideInPresentationAnimator.o <= SlideInPresentationAnimator.swift }Copy the code

You can read Resources [7] for further information.

Build active architecture only

This setting is the default setting, but you should carefully check whether it is strive for. Projects should set Build Active Architecture Only to YES for Debug builds.

For information on what Build Active Architecture Only is, you can read the following [8]

Conditional generation of dSYM files

By default, DWARF with dSYM files are disabled in Debug mode, but crashes that occur in non-debug mode are useful. A simple example is when the test package you called to test crashes…

Since most companies now automatically build projects for distribution to test, you can opt out of generating dSYM files during debugging to speed up your compilation.

For more information on this section, you can refer to the Recommended reference [9]

Third party dependencies

There are three common ways to embed third-party dependencies into a project:

  • CocoaPods: The source file that is compiled each time a clean build of your project is executed
  • Carthage: As a pre-built framework/library
  • Swift Package Manager: A method provided by Apple to add third-party dependencies designed to simplify the process of sharing code and reusing others’ code.

CocoaPods is the most popular dependency manager on iOS, which by design leads to longer build times because in most cases the source code of the third-party library is compiled every time you perform a Clean build. Usually you don’t need to do this very often, but in reality, you do (because of branch switches, Xcode bugs, etc.).

Carthage is expensive to use, but is a better choice if you focus on build time. External dependencies are only built when you change something in the dependency list (add a new framework, update the framework to a newer version, and so on). This can take five to 15 minutes to complete, but it’s much less frequent than building code embedded in CocoaPods. You can even skip the first few minutes using Rome[10].

Swift Package Manager compiles almost as fast as source code, but the files you rely on don’t contain any Objective-C or bundled resources. This can be a good choice for your own projects.

modular

Incremental compilation in Swift is not perfect. In some projects, changing a string in one place causes almost the entire project to be recompiled during an incremental build. You can debug it and improve it, but there is no good tool.

To avoid these problems, consider splitting your application into modules. In iOS, they are: dynamic frameworks or static libraries (Swift support was added in Xcode 9).

Suppose your application target relies on an internal framework called DatabaseKit. The main reason for using this method is to ensure that when you change something in your application project, you do not recompile DatabaseKit during an incremental build.

You may find it helpful to read references [11][12].

XIBs

XIBs/ Storyboards versus pure coding is a tradeoff that we won’t discuss here. But interestingly, when you change the contents of a file in Interface Builder, only the file is compiled (in NIB format). On the other hand, on the other hand, when you change (for example, a line in a public method in a UIView subclass), the Swift compiler may decide to recompile a large part of the project.

If you have any questions about this, you can read resources [13]

Xcode Schemes configuration

Suppose we have a public project setting with three targets:

  • App
  • AppTests
  • AppUITests

It’s ok to just use one solution, but we can do better. The Settings we recently used include three options:

App

When cmD-B builds the application, only unit tests are run. This is useful for short iterations, such as on UI code, where only the required code is built.

APP- Unit testing process

When building applications and unit test targets, only unit tests are run. This is useful when dealing with code related to unit tests, because you find compilation errors in tests immediately after you build the project and don’t even have to run them!

This scenario is useful when UI tests are too long to run often.

APP- All testing processes

Build the application and all test targets, and run all tests. Useful when dealing with UI code that affects UI tests.

If you have any questions about the configuration of Xcode Schemes, check out resources [14]

Use new Xcode to build system

In Xcode 9, Apple introduced a new build system. To enable it, go to the Xcode menu bar -> File -> Workspace/Project Setting. Change Build System to New Build System.

This is the default in Xcode 10. This may cause a Cycle in dependencies between targets… Error when adding dependencies using CocoaPods.

You can see Resources [15] to learn more about him

Display build times in Xcode

Finally, to really know if your build times have really improved, you should enable the display of build times in Xcode’s user interface. To do this, run this command in a command line tool:

$ defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES
Copy the code

When finished, after building the project (CMD-B), you should see:

I recommend comparing build times under the same conditions every time, for example:

  • Exit the Xcode
  • Remove Derived Data$ rm -rf ~/Library/Developer/Xcode/DerivedData
  • Open your project in Xcode
  • Start build as soon as Xcode is opened or as soon as the indexing phase is complete. The first approach seems more representative, because starting with Xcode 9, builds also perform indexing.

Alternatively, you can check build times using the command line:

$ time xcodebuild other params
Copy the code

You can find out how to view build times in Resources [16].

The resources

  1. How Uber Deals with Large iOS App Size: eng.uber.com/how-uber-de…
  2. Optimizing-Swift-Build-Times:github.com/fastred/Opt…
  3. Safeguarding Against Long Compiles: khanlou.com/2016/12/gua…)
  4. Measuring Swift compile times in Xcode September Jesse Squires:www.jessesquires.com/blog/measur…
  5. By Improving Swift compile times – Swift Sundell:www.swiftbysundell.com/posts/impro…
  6. Swift Build Time Optimizations — Part 2: medium.com/swift-progr…)
  7. Diving into Swift Compiler Performance: Koke. me/2017/03/24/…
  8. What is Build Active Architecture Only: samwize.com/2015/01/14/…
  9. Restricted Up Development Build Times With Conditional dSYM Generation: Holko. Pl /2016/10/18/…
  10. Room: github.com/tmspzz/Rome
  11. Technical Note TN2435 – Embedding Frameworks In the An App:developer.apple.com/library/con…
  12. UFeatures:github.com/microfeatur…
  13. (…). Incremental build in a large project is much faster if only A. xib was changed (vs. only a line of Swift UI code: Twitter.com/MichalCiuba…
  14. All About Schemes: pilky.me/17/
  15. Faster Swift Builds with the New Xcode Build System: github.com/quellish/Xc…
  16. How to enable build timing in Xcode? – Stack Overflow:stackoverflow.com/a/2801156/1…

If you have any questions, comments or feedback, please feel free to contact me. If you wish, you can spread the word by sharing this article.

Thank you for reading this! 🚀