Carthage - Decentralized Dependency Manager

Jul 16, 2022iosswiftobjcxcode

Carthage is a dependency manager for Swift and Objective-C projects that aims to provide a simpler tool that’s more flexible and easier to understand and maintain. Here’s how Carthage achieves this:

  • It doesn’t change your Xcode project or force you to use a workspace.
  • You don’t need Podspecs or a centralized repository where library authors submit their pods. If you can build your project as a framework, you can use it with Carthage, which leverages existing information straight from Git and Xcode.
  • Carthage doesn’t do anything magically; you’re always in control. You add dependencies to your Xcode project and Carthage fetches and builds them.

Carthage builds your dependencies and provides you with binary frameworks, but you retain full control over your project structure and setup. Carthage does not automatically modify your project files or your build settings.

Note that Carthage only supports dynamic frameworks, which are only available on iOS 8 or later (or any version of OS X). Using XCFrameworks as of version 0.37.0 (January 2021), and require XCFrameworks when building on an Apple Silicon Mac.

Carthage will check to make sure that downloaded Swift (and mixed Objective-C/Swift) frameworks were built with the same version of Swift that is in use locally. If there is a version mismatch, Carthage will proceed to build the framework from source. If the framework cannot be built from source, Carthage will fail.

Quick Start

  1. Get Carthage by running brew install carthage.
  2. Create a Cartfile in the same directory where your .xcodeproj or .xcworkspace is
  3. List the desired dependencies in the Cartfile, for example:
github "Alamofire/Alamofire" ~> 5.5
  1. Run carthage update --use-xcframeworks
  2. A Cartfile.resolved file and a Carthage directory will appear in the same directory where your .xcodeproj or .xcworkspace is
  3. Drag the built .xcframework bundles from Carthage/Build into the “Frameworks and Libraries” section of your application’s Xcode project.
  4. If you are using Carthage for an application, select “Embed & Sign”, otherwise “Do Not Embed”.

Make sure to commit your Cartfile.resolved, because anyone else using the project will need that file to build the same framework versions.

After you’ve finished the above steps and pushed your changes, other users of the project only need to fetch the repository and run carthage bootstrap to get started with the frameworks you’ve added.

Target iOS

  1. Create a Cartfile that lists the frameworks you’d like to use in your project.
  2. Run carthage update. This will fetch dependencies into a Carthage/Checkouts folder, then build each one or download a pre-compiled framework.
  3. Open your application targets’ General settings tab. For Xcode 11.0 and higher, in the “Frameworks, Libraries, and Embedded Content” section, drag and drop each framework you want to use from the Carthage/Build folder on disk. Then, in the “Embed” section, select “Do Not Embed” from the pulldown menu for each item added. For Xcode 10.x and lower, in the “Linked Frameworks and Libraries” section, drag and drop each framework you want to use from the Carthage/Build folder on disk.
  4. On your application targets’ Build Phases settings tab, click the + icon and choose New Run Script Phase. Create a Run Script in which you specify your shell (ex: /bin/sh), add the following contents to the script area below the shell:
/usr/local/bin/carthage copy-frameworks
  1. Create a file named input.xcfilelist and a file named output.xcfilelist
  2. Add the paths to the frameworks you want to use to your input.xcfilelist. For example:
$(SRCROOT)/Carthage/Build/iOS/Result.framework
$(SRCROOT)/Carthage/Build/iOS/ReactiveSwift.framework
$(SRCROOT)/Carthage/Build/iOS/ReactiveCocoa.framework
  1. Add the paths to the copied frameworks to the output.xcfilelist. For example:
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Result.framework
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ReactiveSwift.framework
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ReactiveCocoa.framework

With output files specified alongside the input files, Xcode only needs to run the script when the input files have changed or the output files are missing. This means dirty builds will be faster when you haven’t rebuilt frameworks with Carthage.

  1. Add the input.xcfilelist to the “Input File Lists” section of the Carthage run script phase
  2. Add the output.xcfilelist to the “Output File Lists” section of the Carthage run script phase

This script works around an App Store submission bug triggered by universal binaries and ensures that necessary bitcode-related files and dSYMs are copied when archiving.

Cartfile

The three supported origins right now are GitHub repositories, Git repositories, and binary-only frameworks served over https.

Carthage supports several kinds of version requirements:

  • >= 1.0 for “at least version 1.0”
  • ~> 1.0 for “compatible with version 1.0”
  • == 1.0 for “exactly version 1.0”
  • “some-branch-or-tag-or-commit” for a specific Git object (anything allowed by git rev-parse). Note: This form of requirement is not supported for binary origins.

If no version requirement is given, any version of the dependency is allowed.

# Require version 2.3.1 or later
github "ReactiveCocoa/ReactiveCocoa" >= 2.3.1

# Require version 1.x
github "Mantle/Mantle" ~> 1.0    # (1.0 or later, but less than 2.0)

# Require exactly version 0.4.1
github "jspahrsummers/libextobjc" == 0.4.1

# Use the latest version
github "jspahrsummers/xcconfigs"

# Use the branch
github "jspahrsummers/xcconfigs" "branch"

# Use a project from GitHub Enterprise
github "https://enterprise.local/ghe/desktop/git-error-translations"

# Use a project from any arbitrary server, on the "development" branch
git "https://enterprise.local/desktop/git-error-translations2.git" "development"

# Use a local project
git "file:///directory/to/project" "branch"

# A binary only framework
binary "https://my.domain.com/release/MyFramework.json" ~> 2.3

# A binary only framework via file: url
binary "file:///some/local/path/MyFramework.json" ~> 2.3

# A binary only framework via local relative path from Current Working Directory to binary project specification
binary "relative/path/MyFramework.json" ~> 2.3

# A binary only framework via absolute path to binary project specification
binary "/absolute/path/MyFramework.json" ~> 2.3

Artifacts

When you run carthage update, Carthage creates a couple of files and directories for you:

  • Cartfile.resolved: This file serves as a companion to the Cartfile. It defines exactly which versions of your dependencies Carthage selected for installation. It’s strongly recommended to commit this file to your version control repository. Its presence ensures that other developers can get started quickly by using the exact same dependency versions.

  • Carthage/Build: This contains the built framework for each dependency. You can integrate these into your project, and you’ll do so shortly. Carthage either builds each framework from source or downloads it from the project’s Releases page on GitHub.

  • Carthage/Checkouts: This is where Carthage checks out the source code for each dependency that’s ready to build into frameworks. Carthage maintains its own internal cache of dependency repositories, so it doesn’t have to clone the same source multiple times for different projects.

Whether you commit the Build and Checkouts directories to your version control repository is up to you. It’s not required, but doing so means that anybody who clones your repository will have the binaries and source for each dependency available.

Having this backup can be a useful insurance policy if GitHub is unavailable or a source repository is removed.