WWDC18: Modern Tips for Optimizing Swift Build Time in Xcode 10
WWDC18: Modern Tips for Optimizing Swift Build Time in Xcode 10
Learn about the new features recently announced in Xcode 10 and Swift 4.2 introduced to make Swift builds faster.
Join the DZone community and get the full member experience.Join For Free
Do you need to strengthen the security of the mobile apps you build? Discover more than 50 secure mobile development coding practices to make your apps more secure.
With Xcode 10, there are some big enhancements in performance and developer productivity while building Swift projects. Build times are boring and might distract developers if builds are taking longer. Apple has put some effort into optimizing the build times, especially for the Swift language. Basically, build times differ depending on the lines of code, the number of lines of code, dependencies, and machine configuration that have been used. There were various new features announced this year both in Xcode 10 and the Swift compiler to make Swift builds quicker than ever. There are various tips and tricks already covered in this GitHub repository but we will cover whats new in Xcode 10 and Swift 4.2 that will help us to make our Swift builds much faster.
1. Understand the Xcode Build Process
The build process is a collection of tasks that have been performed under the hood using native command line tools like swiftc, clang, ld, actool, ibtool, etc. It involves compiling and linking the source code, copying resources like assets, storyboards, code signing and custom things with build scripts, and many more. However, the Xcode build process automates these tasks effectively and in the right order so that we can speed up the build process. The compilation of Swift source files can be done in parallel if possible so that the linker can take all these tasks at once to prepare the executable of the apps. The Xcode build system takes cares of everything from analyzing dependencies to determining which tasks can be run in parallel. It’s also important to understand the concept of the incremental build so that we don’t need to build everything if we are not changing it. You will know more about how the Xcode build process works behind the scene by watching this WWDC session. In order to speed up the build process, we should help build the system to do its job faster by identifying and optimizing dependencies in our apps. That’s why it’s important to understand how the Xcode build process works under the hood.
2. Use the New Build System
Apple launched new build system in Xcode 9, but it was not activated by default. However, with Xcode 10, the new build setting has been activated by default and enabled from Xcode Files-> Project/Workspace Settings.
Check the previous blog post on Xcode's new build system for detailed information about the build system. If you are building an iOS project from the command line using xcodebuild then we have to pass additional parameter -UseNewBuildSystem=YES will also force the new build system. The new build system is called xcbuild. The binary of Apple’s xcbuild is located at this path:
3. Parallelize Swift Builds
Xcode projects are composed of multiple targets; one target may be dependent on another target to build. An Xcode target specifies a product to build; for example, we might have a different target for main app and unit or UI tests. When one target is depending on another target to build, then it creates a dependency to, say, run unit tests for the iOS app. We first need to build the main app and then we can build a unit test target to execute unit tests. In Xcode Build Phases, we can specify the target dependencies explicitly using TargetDependencies and implicitly using Link Binary with libraries.
Building the targets serially will take time and may not be good for utilizing system resources. It would be awesome to build the targets if possible. With Xcode 10, we can parallelize the build and analyze the dependencies under the hood, which increases the build time significantly. We can enable parallel build by editing the Xcode Scheme and checking "parallel builds" in the build action of the scheme.
Make sure you check both "Parallelize Build" and "Find Implicit Dependencies" to make sure we get the best performance out of Xcode. Parallelize Build will make sure that builds are running in parallel if possible and Find Implicit Dependencies will check all the dependencies inside our project, usually at the “Link Binary with Library” build phase. Xcode 10 also introduced the feature of “Parallelized Target Build Process,” which means the dependent target can begin compilation as soon as possible without waiting first to finish; however, it must wait for the “Run Script” phase of the first target.
4. Improve Run Scripts Phase
In Xcode build phases, we can add custom run scripts to make our build process as per our own project needs. A good example would be Carthage — if you have used Carthage for dependency management, you must have experienced the run scripts phase. We have to add a Run Script phase and input and output files, as shown below:
As you can see, we have specified Input Files to the run scripts phase, which is important for the build system to make the decision if run scripts need to be run or not for the dependent target build. It’s always a good idea to provide the input files to the run scripts wherever applicable. When the number of input files has grown, then Xcode 10 gives us a way to specify all the input files in the .xcfilelist format and we can add this file as File List input in the build phase. The Xcode build system will always run this build phase when there are no input files, changed input files, or missing output files. It’s very important to add those files to avoid running this phase for all the incremental builds when it’s not required. There is also documentation in Xcode help for the Run Script phase.
You can read more information about the “Run Script” phase in the documentation.
5. Analyze Build Time
With Xcode 10, we can analyze the build process with timing summary, which helps us to determine where our build is taking the time and take corrective action to reduce the build time. This new feature will show the timing of each task performed by the build system and can be found in the Xcode Product –> Perform Action –> Build with Timing Summary.
This feature can also be enabled using command line using the xcodebuild tool.
$ xcodebuild -showBuildTimingSummary
This will print the build summary timing in the logs. By using this technique, we can analyze what needs to be done in order to make builds faster.
6. Compilation Mode vs Optimization Level
Recently, Apple separated build settings “Compilation Mode” and “Optimization Level” so that we can control the compilation mechanism over the debug builds. The compilation mode is a mode of how our project builds where we can set whether we need to optimize for speed or skip it for building in debug mode. In case of debug builds, the whole optimization is not always required so that we can set this option as incremental for debug builds. In Xcode 10, by default, Compilation Mode is set to “Incremental.” This will speed up the build drastically as the whole module stops doing the incremental builds and builds all the files during the build process. The “Optimization Level” build setting can be set to “No optimization” for debug builds, and for release builds, it can be set to “Optimize for Speed.” You can see that there is a new option introduced in Xcode 10: “Optimize for Size,” which can be used to check the code size. This is machine code generated by compiling the app, not the size of the app or assets.
You can see that Xcode 10 has enabled the right options by default. If you are not using those options, then you should change those options immediately to get the benefits of the new Xcode features.
7. Manage Dependencies Smartly
Every app has either internal or external dependencies that might slow down the build process. It's a good idea to do an inspection of dependencies and sort them out correctly to help the Xcode build system to perform better. The dependencies come from various places in the Xcode project; they might be built-in, target dependencies, or implicit dependencies in the link binary with libraries, build phase dependencies, or scheme order dependencies. We have to manage them well for better speed, which involves
- Choosing the right dependency management solution, Carthage or CocoaPods: check out the previous post to choose between Carthage and CocoaPods.
- Adding implicit dependencies rather than explicit dependencies.
- Managing projects and workspaces so that all the code is easily accessible.
- Determining a strategy to put the dependency source code in the repository or download it dynamically.
- Using a caching strategy of dependency management to speed up the build.
With the combination of Xcode 10 and Swift 4.2, we can take advantage of the new features launched by Apple this year to speed up Swift builds. I hope you like these tips collected from WWDC 2018 to implement in your current project. Let me know what you think of these features of Xcode 10 in the comments below.
Published at DZone with permission of Shashikant Jagtap , DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.