Adding external dependencies
External dependencies are also represented by a graph where nodes represent frameworks, libraries, or bundles. Dependency managers like CocoaPods integrate it when running
pod install leveraging Xcode workspaces, and Swift Package Manager does it at build time leveraging Xcode's closed build system. Both approaches might lead to integration issues that can cause compilation issues down the road. We are aware that's not a great developer experience, and thus we take a different approach to managing external dependencies that allow leveraging Tuist features such as linting and caching. The idea is simple; developers define their Carthage and Package dependencies in a
Dependencies.swift file. They are fetched by running
tuist fetch and integrated into the generated Xcode project at generation time. Because we merge your project and the external dependencies' graph into a single graph, we validate and fail early if the resulting graph is invalid.
As more and more libraries are dropping CocoaPods support, there is no plan to support it in
Dependencies.swift at the moment. You can still use it on top of the tuist generated project by just running
pod install after
If you prefer, you can use the native Xcode native Swift Package Manager support by defining the packages in the
Project.packages property, and adding the dependencies to the target using the
It can be useful in case some dependencies has problem with the
Dependencies.swift integration, or in case you need to use a Swift Package Manager plugin, but doing so you will lose support for important features like caching and package resolution at generation time.
Declaring the dependencies
External dependencies are declared in a
Dependencies.swift file in your project's
Tuist directory at the project's root. If that file doesn't exist, create an empty file, and run
tuist edit to edit its content with Xcode. The snippet below shows an example
Dependencies.swift manifest file:
let dependencies = Dependencies(
.github(path: "Alamofire/Alamofire", requirement: .exact("5.0.4")),
.remote(url: "https://github.com/Alamofire/Alamofire", requirement: .upToNextMajor(from: "5.0.0")),
One of the benefits of using Tuist's built-in support for declaring external dependencies is that the interface is standard across all the supported dependency managers. It makes moving a dependency from a dependency manager to another a non-breaking change.
After dependencies have been declared, you need to fetch them by running
tuist fetch. Tuist will use the dependency managers to pull the dependencies under the
|- Dependencies.swift # Manifest
|- graph.json # stores the serialized dependencies graph generated by `tuist fetch`
|- Lockfiles # stores the lockfiles generated by the dependencies resolution
|- Build # stores content of `Carthage/Build` directory generated by `Carthage`
|- Cartfile # the generated Cartfile
|- .build # stores content of `.build/` directory generated by `Swift Package Manager`
|- Package.swift # the generated Package.swift
We recommend excluding the following files and directories from version control (for example, in your
Tuist/Dependencies/graph.json # Avoid checking in the serialized dependencies graph generated by Tuist.
Tuist/Dependencies/Carthage # Avoid checking in build artifacts from Carthage dependencies.
Tuist/Dependencies/SwiftPackageManager # Avoid checking in build artifacts from Swift Package Manager dependencies.
Integrating dependencies into your project
Once dependencies have been fetched, you can declare dependencies from your projects' targets. Run
tuist edit to edit your project's manifest, and use the
.external target dependency option to declare the dependency.
The snippet below shows an example
Project.swift manifest file:
let project = Project(
deploymentTarget: .iOS(targetVersion: "13.0", devices: .iphone),
Some notes on the integration of Swift packages
- When Swift packages are integrated as source code into your project's graph, no scheme is created for them, as you usually don't want to build only the dependency explicitly. If you need it (for example, for testing some problem in the library), you can create the scheme directly from Xcode, or you can define it in your
Project.swiftif you need it to be available across generations.
- If present, Tuist uses the product type defined in the package manifest file. Otherwise, it defaults to use
.staticFramework. You can override the product type using the
- To use Swift packages from an Objective-C target, add the path to the public headers of the package to the
HEADER_SEARCH_PATHSof the target. The path will be something like
Tuist/Dependencies/SwiftPackageManager/.build/checkouts/<your_package>/<the_headers>(workaround for issue 4180).