Scaling Your Xcode Projects With Tuist

...

Tuist is a tool that makes it easy to generate and scale your Xcode projects. This post will show you how our iOS team uses Tuist and how it can benefit your team as well.

Tuist feature image

One of the biggest challenges for iOS development teams is scaling a codebase when their company or product takes off. Growth and scaling issues are a great thing, but they introduce some typical growing pains as well, like:

  • Clearly defining responsibilities when splitting up your development effort across multiple teams.
  • Decreasing the edit-build-run cycle of each developer as your codebase keeps growing.

Tuist is run by the Tuist organization. Over the years, they have proven to be a stable factor in the iOS developer community. They actually maintain a number of smaller artifacts used by other developers to implement their tools, like Sourcery, ProjLint, XcodeGen, and xspm.

For many reasons, Stream decided to sponsor the Tuist organization. And we hope you do, too. They maintain a couple of vital tools and are working on an excellent way for you to be able to scale your Xcode-based codebase.

The rest of this article will focus onTuist as a tool to help you scale your project. It requires you to make a shift in your mindset about Xcode project files. But once you do, you will reap huge benefits. No more conflicts in Xcode project files, easy and consistent addition, and management of framework configurations. Moving and organizing code becomes a breeze and your compilation times will decrease dramatically.

What Is Tuist?

Tuist helps you maintain your Xcode projects by describing them in the Swift language itself. It allows you to reuse project configurations across projects with ease so you can focus on specific areas of a codebase. You can define your own project of company-specific conventions within Tuist to achieve a consistent structure across multiple projects.

Tuist supports projects comprised of a single target or hundreds of targets (in Xcode, each framework consists of a minimum of one target ). Once you get comfortable with Tuist, there’s no reason not to split your codebase into multiple frameworks promoting proper componentization and isolation of features.

It even addresses some of the complexities involved with managing Xcode projects. You define your project in Swift code and before you start working, you generate your Xcode projects. This last feature probably takes care of over 90% of the merge conflicts you face when working with multiple contributors on an Xcode-based codebase.

Adopting Tuist On Your Project

You can start using Tuist in existing and new projects. The easiest way is obviously to start using Tuist on a new project. But, we have seen successful adoptions of Tuist on existing projects as well.

If you are working on a new project, have a look at the (Getting Started guide by Tuist)[https://docs.tuist.io/tutorial/get-started]. If you want to adopt Tuist on an existing project, have a look at the (Adopting Tuist guide)[https://docs.tuist.io/guides/adopting-tuist].

Integrating Tuist in Your CI Build Environment

Integrating Tuist in any CI Build environment should not pose any problems. After the step of cloning or pulling the source code you need to make sure the Xcode projects are generated by Tuist. Running tuist generate is all it takes to do that. After generating the Xcode project, the CI build environment takes care of the rest.

The same process can be applied when working on the codebase. Obviously, the Tuist executable needs to be available on your CI environment. You can install it as a command line tool on the CI platform, but you can also bundle Tuist as a tool in your project repository.
To bundle Tuist, you need to do a couple of things on your development machine:

  1. Inspect the currently used version of Tuist by running tuist local.
  2. Take the version number you currently use locally and pin your codebase to that version by running tuist local x.y.q, where x.y.z is the current local version of Tuist you want to use.
  3. Run tuist bundle to bundle the Tuist binary.
  4. Commit and push your local changes.

If there is no Tuist installed by default on your CI environment, you do not have to install Tuist. Just change the invocation of Tuist from tuist generate to .tuist-bin/tuist generate. Since that’s now the path where the bundled Tuist version resides within your repository.

What Tuist Can Do for You

Manage Your Frameworks

When working with Xcode projects, quite often you have to deal with third party dependencies through CocoaPods, Carthage, Swift Packages and XCFrameworks. On top of that, splitting up your own codebase into several frameworks is also something that happens. Especially on larger codebases. When working within Xcode, you have to deal with frameworks in all their shapes eventually.

Tuist makes managing frameworks in a consistent way much easier, both for third party and your own frameworks. Tuist is a convention driven tool; in addition to the conventions defined in the tool itself, you can apply your project-specific conventions on top of it. Tuist allows you to structure your code and target configurations in consistent ways. By utilizing conventions, you can quickly move existing code into a new framework and define totally new frameworks within minutes.

Handle Third-Party Dependencies

Third-party dependencies are important when developing iOS apps. It could be a vendor framework or some open-source code saving you from having to write a lot of things yourself. Regardless of what framework you need to integrate, you will be dealing with third-party dependencies through CocoaPods, Carthage, Swift Package Manager, XCFrameworks or maybe even a static library with some header files.

Tuist abstracts away a lot of the details for managing your iOS dependencies. The end result is a concise integrated view on your dependencies and the targets (app target and frameworks) that you yourself are creating.

The benefit of using Tuist for third-party dependencies is that you become dependency-manager agnostic. Any dependency defined with any relevant dependency manager will work.

Cache Pre-Built Dependencies

Compilation times are a big factor when working with Xcode projects, especially when dealing with larger codebases. Part of the power of Swift is its flexibility, but being able to do this brings complexity in the language implementation, complexity you will notice when compiling Swift code. Especially when dealing with protocol conformance, compilation complexity can skyrocket. The best way to deal with compilation time is by not compiling at all. Tuist can help with that by pre-compiling parts of your code and detecting which pre-built dependencies need re-compilation due to source code changes.

Create a Component Based Codebase

Because Tuist allows you to easily work with frameworks, both in creating them and in keeping them consistent across your project, you can divide your code into components by putting code in separate frameworks. Frameworks in Swift allow you to define clear API boundaries within your own codebase. Public, internal, and private scope get meaning.

Decrease Compilation Times

Beyond pre-building parts of a codebase, separating your code into components in itself will decrease compilation time as well. Most likely, you will adopt an approach that hides implementation details within the frameworks that you create by scoping them correctly.

Implementation details that are not publicly available will also not be a factor when compiling against your framework’s interfaces. By having large parts of your implementation only visible inside the framework defining the implementation, you will decrease the work the compiler has to do on your source code.

Conclusions

Adopting Tuist requires effort, and it does change the workflow of every developer. But, being able to easily divide your codebase into several, if not many, frameworks unlocks the ability to start looking at your Xcode projects as true collections of components. Branching off a project component into a full fledged Swift SPM Package is easy when needed, increasing re-use.

On top of that, Tuist allows for improved compilation times because more implementation details are kept within smaller sections of your codebase. The compiler has to look at fewer Swift files at a time when compiling. And if that is not enough, Tuist provides the ability to cache compiled versions of the frameworks you define in your codebase.

All in all, if compilation times ever become an issue on your project, Tuist can provide ways to deal with those issues. And even before that, being able to define your project in a component based way is a huge benefit in itself. Especially when working with multiple people or even multiple teams on the same project.