• Stars
    star
    3,579
  • Rank 12,394 (Top 0.3 %)
  • Language
    Swift
  • License
    MIT License
  • Created about 7 years ago
  • Updated almost 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Collection of advice on optimizing compile times of Swift projects.

Optimizing Swift build times

Collection of advice on optimizing compile times of Swift projects.

Swift is constantly improving ❤️. For the time being, though, long compile times persist as a big issue when working on medium-to-large apps. The goal of this project is to gather all there is that can help you shorten your build times.

👷🏻 Maintainer: Arek Holko. Anything missing? Issues and pull requests welcomed!

Table of contents

Incremental Compilation Mode with No Optimization

Until Xcode 10, it was common to enable Whole Module Optimization to speed up Debug builds. It was a workaround that's no longer needed in Xcode 10!

Currently, the recommended setup is to have Incremental Compilation Mode set for Debug builds and Whole Module for Release builds. Also, No Optimization should be chosen for Optimization Level setting of Debug builds.

📖 Sources:

Type checking of functions and expressions

Swift build times are slow mostly because of expensive type checking. By default Xcode doesn't show code that's slow to compile. You can instruct it to show slowly compiling functions and expressions, though by adding:

  • -Xfrontend -warn-long-function-bodies=100 (100 means 100ms here, you should experiment with this value depending on your computer speed and project)
  • -Xfrontend -warn-long-expression-type-checking=100

to Other Swift Flags in build settings:

Build again and you should now see warnings like these:

Next step is to address code that Swift compiler has problems with. John Sundell and Robert Gummesson are here to help you with that.

📖 Sources:

Slowly compiling files

The previous section described working on an expression- and function-level but it’s often interesting to know compile times of whole files too.

There’s no UI in Xcode for that, though, so you have to build the project from the CLI with correct flags set:

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

(Replace -project YourProject.xcodeproj with -workspace YourProject.xcworkspace if you use a workspace.)

Then extract the interesting statistics using:

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

You’ll end up with slowest.log file containing list of all files in the project, along with their compile times. Example:

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 }

📖 Sources:

Build active architecture only

This setting is a default but you should double check that it’s correct. Your project should build only active architecture in Debug configuration.

📖 Sources:

dSYM generation

By default in new projects, dSYM files aren’t generated at all for Debug builds. However, it’s sometimes useful to have them available when running on a device – to be able to analyze crashes happening without the debugger attached.

Recommended setup:

📖 Sources:

Third-party dependencies

There are two ways you can embed third-party dependencies in your projects:

  1. as a source that gets compiled each time you perform a clean build of your project (examples: CocoaPods, git submodules, copy-pasted code, internal libraries in subprojects that the app target depends on)
  2. as a prebuilt framework/library (examples: Carthage, static library distributed by a vendor that doesn’t want to provide the source code)

CocoaPods being the most popular dependency manager for iOS by design leads to longer compile times, as the source code of 3rd-party libraries in most cases gets compiled each time you perform a clean build. In general you shouldn’t have to do that often but in reality, you do (e.g. because of switching branches, Xcode bugs, etc.).

Carthage, even though it’s harder to use, is a better choice if you care about build times. You build external dependencies only when you change something in the dependency list (add a new framework, update a framework to a newer version, etc.). That may take 5 or 15 minutes to complete but you do it a lot less often than building code embedded with CocoaPods. You can even skip those initial minutes using Rome.

📖 Sources:

  • time spent waiting for Xcode to finish builds 😅

Modularization

Incremental compilation in Swift isn’t perfect. There are projects where changing one string somewhere causes almost a whole project to get recompiled during an incremental build. It’s something that can be debugged and improved but a good tooling for that isn’t available yet.

To avoid issues like that, you should consider splitting your app into modules. In iOS, these are either: dynamic frameworks or static libraries (support for Swift was added in Xcode 9).

Let’s say your app target depends on an internal framework called DatabaseKit. The main guarantee of this approach is that when you change something in your app project, DatabaseKit won’t get recompiled during an incremental build.

📖 Sources:

XIBs

XIBs/storyboards vs. code. 🔥 It’s as hot a topic as they go but let’s not discuss it fully here. What’s interesting is that when you change the contents of a file in Interface Builder, only that file gets compiled (to NIB format). On the other hand, Swift compiler may decide to recompile a big part of your project when you change e.g. a single line in a public method in a UIView subclass.

📖 Sources:

Xcode Schemes

Let’s say we have a common project setup with 3 targets:

  • App
  • AppTests
  • AppUITests

Working with only one scheme is fine but we can do better. The setup we’ve been using recently consists of three schemes:

App

Builds only the app on cmd-B. Runs only unit tests. Useful for short iterations, e.g. on a UI code, as only the needed code gets built.

App - Unit Test Flow

Builds both the app and unit test target. Runs only unit tests. Useful when working on code related to unit tests, because you find about compile errors in tests immediately after building a project, not even having to run them!

This scheme is useful when your UI tests take too long to run them often.

App - All Tests Flow

Builds the app and all test targets. Runs all tests. Useful when working on code close to UI which impacts UI tests.

📖 Sources:

Use the new Xcode build system

In Xcode 9 Apple introduced a new build system. To enable it, go to Workspace or Project Settings from the File menu in Xcode. There you can switch build systems to the new build system.

📖 Sources:

Showing build times in Xcode

Finally, to be able to actually know whether your build times are improving, you should enable showing them in Xcode’s UI. To do that, run this from the command line:

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

Once done, after building a project (cmd-B) you should see:

I recommend comparing build times under same conditions each time, e.g.

  1. Quit Xcode.
  2. Clear Derived Data ($ rm -rf ~/Library/Developer/Xcode/DerivedData).
  3. Open your project in Xcode.
  4. Start a build either immediately after Xcode opens or after indexing phase completes. The first approach seems to be more representative because starting with Xcode 9 building also performs indexing.

Alternatively, you can time builds from the command line:

$ time xcodebuild other params

📖 Sources:

More Repositories

1

MotionBlur

MotionBlur allows you to add motion blur effect to iOS animations.
Objective-C
1,505
star
2

AHKActionSheet

An alternative to the UIActionSheet inspired by the Spotify app.
Objective-C
1,162
star
3

IBAnalyzer

Find common xib and storyboard-related problems without running your app or writing unit tests.
Swift
955
star
4

SloppySwiper

UINavigationController delegate that allows swipe back gesture to be started from anywhere on the screen (not just from the edge).
Objective-C
806
star
5

DeallocationChecker

Catch leaking view controllers without opening Instruments.
Swift
793
star
6

AHKBendableView

UIView subclass that bends its edges when its position changes.
Swift
592
star
7

EmojiTextView

Tap to swap out words with emojis. Inspired by Messages.app on iOS 10.
Swift
338
star
8

ReflectableEnum

Reflection for enumerations in Objective-C.
Objective-C
330
star
9

ConfigurableTableViewController

Typed, yet Flexible Table View Controller
Swift
269
star
10

AHKNavigationController

A UINavigationController subclass that re-enables the interactive pop gesture when the navigation bar is hidden or a custom back button is used.
Objective-C
232
star
11

HamburgerButton

Animated hamburger button.
Swift
197
star
12

BouncyView

Action Sheet animation based on Skype's iOS app.
Swift
128
star
13

AHKSlider

A UISlider subclass that improves the precision of selecting values.
Objective-C
59
star
14

SwiftOutTimingFunction

SwiftOutTimingFunction brings Google's new *Swift Out* animation curve to iOS.
Objective-C
44
star
15

AHKBuilder

Initialization for immutable objects based on the builder pattern.
Objective-C
39
star
16

SingleFetchedResultController

Like NSFetchedResultsController but for a single managed object.
Swift
26
star
17

StoryboardDependencyInjectionTemplates

Code generation of initializers for storyboard-based view controllers
HTML
19
star
18

BezierCurveFit

A simple app for fitting Bezier curve based on data points from another curve.
Objective-C
9
star
19

sicp

My solutions to exercises from "Structure And Interpretation Of Computer Programs"
Scheme
5
star
20

cool-compiler-examples

MIPS assembler files generated by my COOL compiler
Assembly
4
star
21

epidemic-simulation

Source code of a research project based on the use of cellular automata.
JavaScript
2
star
22

ContentOffsetJump

Source code of my answer: http://stackoverflow.com/a/19344402/1990236
Objective-C
1
star