Carthage: what is it, decentralized dependency manager

Author: IT Sectr Published: 2026-02-12 Reading time: 8 min

Carthage is a decentralized dependency manager for Cocoa projects (iOS, macOS, watchOS, tvOS) that builds binary frameworks from source code. Unlike CocoaPods, Carthage does not modify the project automatically — the developer manually adds the built frameworks to Xcode. Carthage is written in Swift, uses Cartfile to describe dependencies, and supports parallel builds. According to the GitHub repository, Carthage has collected over 15,000 stars and remains a niche but sought-after tool for projects that require minimal interference with Xcode configuration.

Key Takeaways

  • Carthage is a decentralized dependency manager: no central registry, libraries are connected directly from Git repositories
  • Cartfile is a configuration file that lists dependencies, their versions, and sources (Git, GitHub, GitLab)
  • Building frameworks is done with carthage bootstrap or carthage update — Carthage clones repositories and compiles them into .xcframework
  • Xcode integration is manual: the developer adds built frameworks to General → Frameworks, Libraries, and Embedded Content
  • Cartfile.resolved locks exact dependency versions, ensuring build reproducibility similar to Podfile.lock
  • Carthage vs CocoaPods vs SPM: Carthage gives maximum control but requires more manual work; CocoaPods automates everything; SPM is built into Xcode

What is Carthage?

Carthage is a dependency manager with a decentralized architecture, created in 2014 by developers from the Swift community. Carthage does not use a central specification registry — each library is connected directly from a Git repository via URL or GitHub name. Carthage downloads the source code, builds it into a binary framework (.xcframework or .framework), and provides the developer with a ready-made artifact for manual integration into an Xcode project.

Carthage's architecture includes three components: the CLI tool carthage, the configuration file Cartfile, and the Carthage/Build/ directory with built frameworks. The key difference between Carthage and CocoaPods is the absence of automatic modification of .xcodeproj. Carthage does not create .xcworkspace, does not configure compiler flags, and does not generate Pods.xcconfig. The developer manually adds frameworks to the project via Xcode, providing full control over the integration process.

Carthage uses parallel dependency builds, which significantly speeds up the process on multi-core processors. Each dependency is built as a separate target, and Carthage automatically resolves the graph of transitive dependencies, building them in the correct order. According to community benchmarks, Carthage builds 15–20 dependencies in an average of 30–60 seconds on modern Macs, which is faster than CocoaPods for projects with many libraries. Carthage supports all Apple platforms: iOS, macOS, watchOS, and tvOS, and since version 0.38+ — building universal .xcframework for simulator and Apple Silicon device support.

How Carthage works

Carthage clones the Git repository of each dependency, switches to the specified version (tag, commit, or branch), and runs xcodebuild to build the framework. Carthage automatically determines the Xcode project type (framework, dynamic framework, static library) by the build scheme. If a project has multiple schemes, Carthage uses the default scheme (first in alphabetical order). After building, Carthage copies the finished framework to Carthage/Build/ and creates a Cartfile.resolved file with exact version locking. Carthage supports caching of built frameworks — rebuilding without dependency changes is skipped.

Transitive dependencies in Carthage are handled through Cartfile.resolved: Carthage builds a graph of all required dependencies and builds them in the correct order. If two libraries depend on the same third-party library, Carthage builds it once and uses it for both. Carthage reports build errors indicating the specific target and cause — this simplifies issue diagnosis.

Cartfile: structure, syntax and examples

Cartfile is a configuration file in Ruby-like syntax (Cartfile format) that defines the dependencies of a Carthage project. Cartfile is located in the project root next to .xcodeproj. Each line of Cartfile describes one dependency: the source (Git URL, GitHub repository) and version. The syntax supports version pinning through tags, commits, and branches.

ruby
# Base dependencies Carthage
github "Alamofire/Alamofire" ~> 5.9
github "SnapKit/SnapKit" ~> 5.7
github "onevcat/Kingfisher" == 8.0.0

The github "Owner/Repo" directive is a shorthand for GitHub repositories. Carthage automatically builds the URL https://github.com/Owner/Repo.git. For GitLab, Bitbucket, and other Git hosts, the full URL is used: git "https://gitlab.com/owner/repo.git". Version operators: ~> 5.9 (any version from 5.9 up to 6.0, excluding 6.0), == 8.0.0 (exact version), >= 1.0 (minimum version). You can pin a specific commit via github "owner/repo" "abc1234".

Complete Cartfile example

Carthage supports multiple directories for different configurations: Cartfile (main), Cartfile.private (for internal dependencies that are not published), and Cartfile.resolved (automatically generated). Private dependencies are useful for libraries used only in development builds, such as test frameworks.

ruby
# Cartfile — core dependencies
github "Alamofire/Alamofire" ~> 5.9
github "SwiftyJSON/SwiftyJSON" ~> 4.0
github "realm/realm-swift" ~> 10.0

# Full URL for GitLab
git "https://gitlab.com/company/internal-lib.git" == 2.1.1

# Development branch
github "marmelroy/PhoneNumberKit" "development"

github and git are two source types in Cartfile. The first is exclusively for GitHub and automatically generates the URL. The second is for any public or private Git repositories with a full URL. A version can be specified as a tag (== 2.1.1), semantic range (~> 5.9), branch name ("development"), or commit hash ("a1b2c3d"). Semantic ranges (~>) are recommended for dependencies that follow SemVer — this protects against breaking changes during updates.

Cartfile.resolved is automatically generated after carthage update. It locks the exact versions of all installed dependencies, including transitive ones. This file should be kept in Git — without it, the carthage bootstrap command on another machine will build libraries by the same rules, but versions may differ. carthage outdated shows a list of outdated dependencies for which new versions are available.

Installing and configuring Carthage

Carthage is installed via Homebrew — the standard package manager for macOS. Alternative methods: installing from a built .pkg installer from GitHub or building from source. Carthage requires Xcode with Command Line Tools (including xcodebuild), and on Apple Silicon Mac — Rosetta 2 for some legacy dependencies.

bash
# Installing Carthage via Homebrew
brew install carthage

# Version check
carthage version

# Installing from .pkg (if Homebrew is unavailable)
# Download Carthage.pkg from GitHub Releases and install it manually

After installing Carthage, project initialization begins with creating a Cartfile in the project root. Carthage has no init command — the file is created manually in a text editor. After populating the Cartfile with dependencies, the developer runs carthage bootstrap (if Cartfile.resolved already exists) or carthage update (initial installation or update). Carthage clones the repositories, builds the frameworks, and places them in Carthage/Build/.

Updating Carthage is done via brew upgrade carthage. Version is checked with carthage version. The latest stable version as of mid-2025 is 0.40 with default .xcframework support, improved parallel builds, and full Swift 6 support. Starting from version 0.39, Carthage stopped building legacy .framework without a compatibility shim — it is recommended to explicitly specify --use-xcframeworks.

bash
# Updating Carthage via Homebrew
brew upgrade carthage

# Install a specific version
brew install carthage@0.39

# Complete reinstallation
brew uninstall carthage && brew install carthage

Note: Carthage does not create .xcworkspace and does not modify .xcodeproj. Unlike CocoaPods, Carthage leaves full control of Xcode configuration to the developer. This means that after installing dependencies, you need to manually add the frameworks to Xcode (see the «Integrating Carthage frameworks into Xcode» section). Carthage also requires that each dependency contains an Xcode project or workspace with a framework target — otherwise the build will fail.

Building frameworks: bootstrap and update

Carthage offers three main commands for working with dependencies: bootstrap, update, and build. carthage bootstrap builds dependencies from an existing Cartfile.resolved — recommended for CI environments and developers joining the project. carthage update updates Cartfile.resolved to the latest versions (respecting Cartfile constraints) and performs the build. carthage build builds all specified dependencies without saving versions.

bash
# Initial installation (updates versions)
carthage update --use-xcframeworks --platform iOS

# Rebuild with locked versions
carthage bootstrap --use-xcframeworks --platform iOS

# Build only one dependency
carthage build Alamofire --platform iOS

The --use-xcframeworks flag tells Carthage to build universal .xcframework instead of legacy .framework. This ensures support for both the simulator and a real device, as well as Apple Silicon Macs without additional scripts. The --platform iOS flag limits the build to a single iOS platform — this significantly speeds up the process, especially if the project includes cross-platform libraries.

Carthage supports parallel building via the --cache-builds flag, which caches already built frameworks. On rebuild, Carthage checks the Git commit hash and, if the code has not changed, skips compilation. For CI servers, it is recommended to cache the Carthage/Build/ directory and ~/Library/Caches/carthage/. Carthage also supports --verbose for detailed logging and --no-use-binaries for forced building from source (if the developer does not trust pre-built binaries).

CommandAction
carthage updateUpdates Cartfile.resolved and builds all frameworks
carthage bootstrapBuilds frameworks from existing Cartfile.resolved without updating
carthage buildBuilds specified dependencies without locking versions
carthage outdatedShows a list of dependencies with available updates
carthage checkoutOnly clones repositories without building

Integrating Carthage frameworks into Xcode

Integration of Carthage frameworks into Xcode is done manually in four steps. After running carthage update or bootstrap, all built frameworks are located in Carthage/Build/iOS/ (or the corresponding platform). The developer opens the Xcode project, selects the app target, and adds the frameworks to General → Frameworks, Libraries, and Embedded Content. For runtime frameworks (dynamic libraries), you must select «Embed & Sign» — otherwise the app will crash on launch with the error «dyld: Library not loaded».

Carthage for static libraries is simpler — they do not require an embed phase since they link directly into the app's executable file. However, Carthage builds dynamic frameworks by default (except for explicitly configured static libraries). For projects where minimizing app size is important, static linking via Xcode settings is recommended.

An additional step is adding Input Files in Build Phase → Run Script. Carthage requires a script to remove simulator artifacts from the built framework (strip simulator architectures). This script is necessary for App Store builds:

bash
# Run Script for App Store (strip simulator architectures)
FRAMEWORKS_DIR="${SRCROOT}/Carthage/Build/iOS"
for framework in "$FRAMEWORKS_DIR"/*.framework; do
  bash "$BUILD_DIR/src/scripts/strip-framework.sh" "$framework"
done

Carthage does not require using .xcworkspace — all dependencies are already built into binary frameworks. Carthage works directly with .xcodeproj, unlike CocoaPods, which creates a workspace. This simplifies version control and CI setup because Carthage dependencies do not change the Xcode project configuration. The only change is adding frameworks to the target, which is recorded in .pbxproj.

StepAction
1Run carthage update --use-xcframeworks
2Drag frameworks from Carthage/Build/ to General → Frameworks
3Set Embed & Sign for dynamic frameworks
4Add Run Script Phase to remove simulator architectures
5Build the project — frameworks should link automatically

Carthage vs CocoaPods vs Swift Package Manager

Carthage, CocoaPods, and Swift Package Manager (SPM) are the three main dependency managers in iOS development. Carthage stands out with its decentralized approach, CocoaPods offers a centralized registry, and SPM is Apple's built-in solution. The choice between them depends on project requirements, team size, and the desired level of automation.

CriterionCarthageCocoaPodsSPM
ArchitectureDecentralizedCentralized registryIntegrated in Xcode
Configuration languageCartfile (Ruby-like)Podfile (Ruby DSL)Package.swift (Swift)
Xcode integrationManual (drag & drop)Via workspaceBuilt-in
Transitive dependenciesAutomaticAutomaticAutomatic
Library registryNone (Git repositories)100,000+ in Specs~65,000
Resource supportNoYes (resource bundles)Yes (Resources)
Build speedFast (parallel)AverageFast
Integration controlFullAutomaticAutomatic

Carthage is chosen for projects that require minimal interference with Xcode configuration and full control over the integration process. Carthage is ideal for open-source libraries and frameworks where the author wants to let users build dependencies independently. Carthage is also popular among developers who value UNIX philosophy: each tool does one thing well. CocoaPods remains the standard for enterprise projects with dozens of dependencies where automation is important. SPM is the choice for new projects as it is built into Xcode and actively developed by Apple.

Migration between managers requires different approaches. Carthage → SPM: remove frameworks from Xcode, delete Cartfile, and add Package Dependencies via File → Add Package Dependencies. Carthage → CocoaPods: remove Carthage frameworks, create a Podfile, add dependencies, and run pod init && pod install. When migrating from Carthage to CocoaPods or SPM, the need to manually update frameworks disappears — all dependencies are updated with a single command. Carthage remains relevant for projects where it is important to avoid vendor lock-in and maintain transparency of dependency builds.

Common problems and their solutions

Carthage is a stable tool, but developers periodically encounter common issues, especially when building on CI servers, updating Xcode, or changing Swift versions. Most problems are solved by clearing the cache, properly configuring --use-xcframeworks, and checking the minimum iOS version.

Error «The file manager returned an error» — occurs when the Carthage cache is corrupted or there is a file permission conflict. Solution: delete the cache with rm -rf ~/Library/Caches/carthage and restart carthage bootstrap. Also helpful is deleting the Carthage/ directory in the project and rebuilding. On CI servers, the Carthage cache should only be updated when Cartfile.resolved changes.

Error «No such module» — the framework is not found in Xcode even though the Carthage build succeeded. Solution: check the framework path in General → Frameworks, Libraries, and Embedded Content. The framework should be in Carthage/Build/iOS/. Ensure that .xcframework is added correctly (drag it again). For dynamic frameworks, check Embed & Sign. If the error persists, add FRAMEWORK_SEARCH_PATHS in Build Settings.

Build error due to Swift incompatibility — the library was built for a different Swift version than the project. Solution: use carthage update --no-use-binaries to force building from source with the same Swift version. If the library does not compile under the current version, use .xcconfig to specify the Swift version or fork the library. Since Carthage 0.39, --use-xcframeworks automatically includes the correct Swift version in the binary.

CI build issues — Carthage on CI requires proper cache configuration. Solution: cache Carthage/Build/ and ~/Library/Caches/carthage/. Use carthage bootstrap --use-xcframeworks --platform iOS instead of update on CI to avoid changing versions. An official Carthage action is available for GitHub Actions. For Jenkins — the CarthageBuild plugin. Carthage may crash on macOS without GUI — solution: install brew install xcode-build-server or add the -UseModernBuildSystem=NO flag.

ProblemCauseSolution
File manager errorCorrupted cacheClear ~/Library/Caches/carthage/
No such moduleFramework not added in XcodeCheck Frameworks in target
Swift incompatibilityDifferent Swift versions--no-use-binaries or newer Carthage version
CI errorMissing cache or GUIConfigure Carthage/Build/ cache
Library does not buildNo Xcode project for the libraryCheck repository structure

Frequently Asked Questions

What is Carthage and how is it different from CocoaPods?

Carthage is a decentralized dependency manager for Apple platforms. Unlike CocoaPods, Carthage does not use a central library registry, does not modify the Xcode project automatically, and does not create .xcworkspace. Carthage builds dependencies into binary frameworks that the developer manually adds to Xcode. CocoaPods, on the other hand, automates the entire process through Podfile.

How to install Carthage on macOS?

Carthage is installed via Homebrew: brew install carthage. Alternatively, you can download Carthage.pkg from GitHub Releases or build from source. After installation, check the version: carthage version. Carthage requires Xcode with Command Line Tools. On Apple Silicon Mac, Rosetta 2 may additionally be required.

How is Cartfile different from Cartfile.resolved?

Cartfile is a configuration file written by the developer: it lists library names and version operators (~> 5.9, == 8.0.0, branch name). Cartfile.resolved is automatically generated during carthage update and locks the exact versions of all installed dependencies. Cartfile.resolved should be kept in Git — it ensures build reproducibility across all machines.

Why won't Carthage build a library from my Cartfile?

Carthage requires that the library contains a valid Xcode project or workspace with a framework target. Check that the repository is accessible (not private without a key), the correct version is specified (tag or commit exists), and the library supports your Xcode version. Use carthage build --verbose for detailed diagnostics. If the library has no framework target, Carthage cannot build it.

Should I use Carthage in 2025–2026?

Carthage remains relevant for projects that require decentralized dependency management, full control over integration, and minimal interference with the Xcode project. However, most new projects choose Swift Package Manager (SPM) — it is built into Xcode, requires no additional installation, and is actively developed by Apple. Carthage is recommended for legacy projects where the build pipeline is already established, or for libraries whose authors want to give users the freedom to choose the integration method.

Summary

  • Carthage is a decentralized dependency manager for iOS, macOS, watchOS, and tvOS that builds frameworks from Git repository sources
  • Cartfile is a configuration file with syntax supporting GitHub repositories, arbitrary Git URLs, and semantic versioning
  • Installation is done via brew install carthage, and dependency builds via carthage bootstrap or carthage update
  • Xcode integration is manual: frameworks are added to General → Frameworks, Libraries, and Embedded Content with the Embed & Sign option
  • Cartfile.resolved locks exact versions of all dependencies, ensuring build reproducibility on CI and all team machines
  • Common issues (cache, Swift incompatibility, CI errors) are resolved by clearing cache, the --no-use-binaries flag, and CI cache configuration
  • Choosing a manager: Carthage — for full control, CocoaPods — for automation, SPM — for new projects with built-in integration

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also