**How We Improved Build Times by Migrating to Tuist and Splitting Code Into Modules**
Six months ago, it took around 45 seconds to make changes to a feature in the Asana iOS app and rebuild it. Today, that time has been significantly reduced to just 15 seconds. The Mobile Foundations team achieved this by migrating their Xcode project to Tuist, a command-line tool that automates the creation of Xcode projects and workspaces, and splitting the code into several modules. In this article, we’ll explore the decision-making process that led to this solution and how you can make similar changes in your own codebase.
**The Initial Challenge**
At the beginning of 2022, the Asana team had around ten engineers maintaining approximately 300,000 lines of Swift code. All of this code was compiled into a single build target, causing build times to increase as the team grew and more code was written. Merge conflicts in the Xcode project file also occurred when two people made simultaneous changes, adding additional time and complexity to the workflow.
**Recognizing the Need for Change**
Anticipating further team expansion and an increase in codebase size, it became clear that build times would continue to be a pain point. To address this, the team needed to find a way to build less code during each iteration. One effective approach was to split the code into modules, allowing for more granular compilation.
**Choosing the Right Tool**
With the goal of creating modules, the team explored four potential options: Xcode’s built-in GUI, Xcode’s native Swift Package Manager support, Bazel, and Tuist. While developer experience was a key criterion, considerations such as adoption cost and support for remote build caching and selective test execution were also factored in.
The Xcode GUI appeared to have the lowest initial cost, but it lacked automation and would require significant manual configuration. The team then prototyped the use of Xcode’s native Swift Package Manager support, but found the developer experience subpar and error-prone. Bazel, a popular choice for managing iOS builds, proved to be too heavyweight and caused workflow-breaking issues. Finally, Tuist emerged as a viable option with minimal downsides.
**Benefits of Tuist**
Tuist, a command-line tool, simplifies the creation of Xcode projects and workspaces. It allows Swift code in a Tuist manifest to access the Foundation framework and offers extensive automation capabilities based on the filesystem. By leveraging Tuist, the team aimed to automate most tasks and provide an effortless developer experience. Tuist also offered advantages such as the ability to revert to an Xcode project if needed and excellent documentation. Additionally, being written in Swift made it possible for the team to contribute code back to the Tuist project if necessary.
**Migrating to Tuist**
One limitation the team encountered with Tuist was the lack of first-party support for CocoaPods. To overcome this, they migrated all CocoaPods dependencies to either Swift Package Manager or Carthage, both of which were supported by their dependencies. Next, they migrated build settings to .xcconfig files using the `tuist migration settings-to-xcconfig` command. With these preparations complete, the team started writing the Tuist manifest, a project definition, from scratch. This process took a couple of weeks, with ongoing tweaks and adjustments made over the following months to resolve any errors that were not directly attributed to Tuist.
**Refactoring for Module Support**
With all the code residing in a single module, the team had never needed to use the `public` keyword. As they began splitting the code into modules, they faced the task of making thousands of classes, methods, and properties public. To automate this process, the team developed a Python script that parsed Swift code and marked internal symbols as public. They also had to address circular dependencies, like the one between the project view and task view in Asana. The team adopted a pattern similar to Tuist’s µFeatures architecture, which placed a feature’s public interface in its own module. Refactoring the code to support this pattern proved to be a labor-intensive task but was necessary to enable module-based development.
**Creating the Modules**
After completing the Tuist manifest, the team moved almost all of the code, except for AppDelegate, into a module called AsanaCore. This allowed them to identify and resolve workflow issues early on. To create the modules, static libraries were chosen as the preferred approach. Several workflow issues, including challenges with NSKeyed[Un]Archiver and XLIFF string exports from Xcode, were encountered and addressed using Python code patches. Gradually, the team transitioned different parts of the codebase into separate modules, completing the process over several months.
**Achieving Faster Build Times**
By migrating to Tuist and splitting the code into modules, the Asana Mobile Foundations team significantly reduced build times. Tasks that previously took 45 seconds now take only 15 seconds, improving developer productivity and overall efficiency. The team successfully automated various processes, refined their coding practices, and contributed to the Tuist project’s ecosystem. These changes have enabled them to accommodate team growth and future codebase expansion while maintaining optimal build performance.