Skip to Content
Course content

237: Swift Build (Experimental Build System)

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why bother with the CLI build system over just hitting "Run" in Xcode?

I get it—Xcode's "Play" button is addictive. It handles the build, the launch, and the debugger in one click. But when you're working on larger systems or setting up a CI/CD pipeline, you can't rely on a GUI. The experimental build system provided by the Swift toolchain (which powers swift build) is designed to be deterministic and portable. It strips away the "magic" of .xcodeproj files, which are notoriously difficult to merge in Git.

When I'm debugging a weird build failure that only happens on the build server and not on my MacBook, the first thing I do is drop into the terminal. By using the command line, you see the raw compiler invocations. You get a clear view of exactly which flags are being passed to swiftc, which is often hidden behind several layers of abstraction in Xcode's Build Settings.

How do I pass specific compiler flags without an Xcode project file?

This is where things get a bit tricky because you can't just click a checkbox in a "Build Settings" tab. If you need to pass a flag directly to the Swift compiler—say, you're using an experimental feature or need to disable certain warnings for a legacy module—you use the -Xswiftc flag. This tells the build system, "Take the next argument and hand it directly to the compiler."

For example, if I'm working on a high-performance math library and I want to enable whole-module optimization to squeeze out more speed, I'd run it like this:

swift build -c release -Xswiftc -whole-module-optimization

If you're dealing with a mixed-language project and need to pass something to the C compiler, you'll use -Xcc. I've found that being explicit with these flags in a shell script is way more reliable than hoping your teammates have the same "User-Defined Settings" configured in their local Xcode environment.

Where does the build system actually put the binaries?

If you're used to the Xcode DerivedData folder, the Swift build system's layout will feel a bit sparse. By default, it creates a hidden .build directory in your project root. Inside there, you'll find a debug or release folder depending on the configuration you chose.

I often see developers get confused when they can't find their executable. If you've built a target called MyNetworkTool, the binary isn't just floating in the root; it's buried in .build/debug/MyNetworkTool. To avoid hunting through folders, you can use the --show-bin-path flag:

swift build --show-bin-path

I usually pipe this into a variable in my automation scripts so I can run tests or benchmarks immediately after the build completes without hardcoding a path that might change based on the OS.




📋 Practical Task

Exercise: Manually Optimizing a Matrix Math Module

In this exercise, you will move away from the IDE to build a performance-critical module using the Swift CLI build system.

  • Create a new directory named MathOptimizer and initialize it as a Swift package: swift package init --type executable.
  • In main.swift, implement a simple loop that performs 10 million additions to simulate a heavy workload.
  • Run a standard debug build using swift build and time how long the resulting binary takes to execute.
  • Now, rebuild the project using the -c release configuration and pass the -O (optimize for speed) flag explicitly using -Xswiftc -O.
  • Locate the release binary using swift build --show-bin-path and compare the execution time against the debug version.
  • Document the time difference; you should see a significant performance boost in the release build.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.