Migrating An Xcode Command Line Tool Project to SPM

Here's how the move to SPM now makes it easy to manage and write tests for a command line tool written in Swift.

Migrating An Xcode Command Line Tool Project to SPM

By Ravi Tripathi

Creating an executable Swift Package is a great way to quickly build a command line tool for folks familiar with the language. Antoine van der Lee has written a great article on getting started with one.

We moved Sonar, an internal command line tool which we use here at Gojek to a Swift Package from an Xcode Project. Moving away from an Xcode project presented some difficulties, requiring some workarounds which we would talk about in this post. But before jumping into that, let’s address a common query.

But why not stick to an Xcode Command Line Target?

Using SPM for building a command line tool instead of an Xcode Command Line target has some benefits:

  • Since the Swift Toolchain is available on Windows and Linux, you can compile your Swift Package on these platforms without any Xcode-specific tools. Use swift build and swift test to build and test a swift package respectively.
  • Adding test cases for Command Line Tool target in Xcode has some caveats, and often involves creating a separate dynamic framework for enclosing your core logic so that it can be unit tested independently. This is not a problem with Swift Packages, where a test target can be added for an executable.

A command line tool is usually a single compiled executable, and having an additional .framework carrying the core logic makes it difficult to distribute. It also makes further optimizations difficult.

However, tools associated with Xcode are more mature when compared to SPM, which can make it difficult to migrate projects.

Moving to SPM

This was really straightforward. In a new directory, run:

swift package init --type executable <Your_Executable_Name>

This generates the Sources and Tests directory, a README.md and a Package.swift file. All we had to do now was move the project files into Sources and the tests into Tests directory respectively.

Opening the Package.swift launches Xcode. After adding the dependencies in the Package.swift file, we were good to go.

Coverage Reports for Swift Packages

While Xcode displays code coverage percentage for both SPM-based and .xcodeproj projects within the IDE, many teams rely on tools like xcov to generate test coverage reports, and even fail CI/CD pipelines if the total coverage drops below a certain limit. This is where we hit our first wall.

Unlike Xcode Projects, no .xccoverage files are generated while running tests for a Swift Package. Instead, SPM generates a .jsonfile containing the coverage data, which is not compatible with Xcov. This prevents us from checking the coverage percentage on CI jobs.

Thankfully, we stumbled upon swift-test-codecov, another command line swift package which can parse this json and generate a table containing coverage results. Coupling this with Mint allowed us to parse the coverage report on CI:

Running swift-test-codecov to parse coverage data for a Swift Package

Exporting the final executable

This was the easy part. swift build --configuration release builds the project and generates the executable inside the .build/release directory. And that’s it! Copy the executable to /usr/local/bin and you are good to go.

However, even on release configuration, the final executable size was well above 12 MB. Quite high for a command line utility. We began looking for ways to compress an executable.


upx is powerful tool which can compress executables on Windows, macOS and Linux. This was perfect for our use case. Running upx brought down the final executable size from 13.7 MB to 3.2 MB, a 76% decrease!!

Ship it 📦

For installing locally, a shell script can encapsulate all the steps needed for building and moving the final executable to /usr/local/bin:

Since we now had a single executable, we switched over to homebrewfor distributing Sonar. This helped us simplify our release mechanism drastically, eliminating the need for checking in a binary in multiple repositories.

SPM now makes it easy to manage and write tests for a command line tool written in Swift. And with tools like swift-test-codecov, upx, homebrew and Mint, you can generate coverage reports for your project, optimize the size of your executable, and provide an easy way to distribute it.

Find more stories from our vault, here.
Check out open job positions by clicking below: