Dependency managers simplify and standardize fetching third-party code into your project. Without one, you’d manually copy sources, drop in pre-compiled binaries, or use Git submodules.
Adding dependencies to a project has coordination costs. A package manager automates downloading, building, and resolving the entire dependency graph while minimizing version conflicts.
SemVer uses MAJOR.MINOR.PATCH — increment MAJOR for breaking changes, MINOR for backward-compatible additions, PATCH for bug fixes. SwiftPM and CocoaPods both rely on SemVer for dependency resolution.
Binary-only packages are less portable than source packages. In SwiftPM, binary targets are primarily an Apple-platform distribution mechanism through .xcframework artifacts declared with .binaryTarget in the manifest. Prefer source-based dependencies when possible.
Git submodules embed one repo inside another. They’re simple but have sharp edges: collaborators must remember git submodule update, they don’t resolve transitive dependencies, and managing rapid co-development across repos is painful. Modern package managers are almost always a better choice.
SwiftPM is Apple’s officially supported dependency manager, built into the Swift project. It was introduced in Swift 3.0 (2015) and gained full Xcode integration with Xcode 11 (2019). Today, most popular iOS libraries support SwiftPM as their primary distribution method.
A Swift package is defined by a Package.swift manifest, using swift-tools-version to specify the tool version:
// swift-tools-version:6.0
import PackageDescription
let package = Package(
name: "DeckOfPlayingCards",
platforms: [.iOS(.v17), .macOS(.v14)],
products: [
.library(name: "DeckOfPlayingCards", targets: ["DeckOfPlayingCards"])
],
dependencies: [
.package(url: "https://github.com/apple/example-package-fisheryates.git", from: "2.0.0"),
.package(url: "https://github.com/apple/example-package-playingcard.git", from: "3.0.0"),
],
targets: [
.target(
name: "DeckOfPlayingCards",
dependencies: ["FisherYates", "PlayingCard"]),
.testTarget(
name: "DeckOfPlayingCardsTests",
dependencies: ["DeckOfPlayingCards"]),
]
)Key modern additions:
platforms — declare minimum OS versions explicitly..binaryTarget — include pre-built .xcframework binaries for proprietary or closed-source dependencies..product(name:, package:) for multi-package disambiguation.Go to File > Add Package Dependencies…, paste a Git URL, and choose a version rule (exact, up-to-next-minor, range, or branch/commit). Xcode resolves the graph, fetches sources, and integrates the target into your project — no workspace required.
The Swift Package Index is a community-run search engine that indexes packages supporting SwiftPM. It shows compatibility badges, test status, and version history to help evaluate quality before adopting a dependency.
CocoaPods manages dependencies via a Podfile and an auto-generated Xcode workspace. It was the dominant iOS dependency manager from ~2013 to 2020 and still hosts the largest catalog of iOS libraries, but adoption is declining as authors prioritize SwiftPM.
CocoaPods is written in Ruby. Install it without sudo using Homebrew or RubyGems:
brew install cocoapods
# or
gem install cocoapods --user-installSpecify dependencies in a Podfile:
platform :ios, '17.0'
target 'MyApp' do
pod 'Alamofire', '~> 5.5'
pod 'SwiftLint', '~> 0.55'
endRun pod install to resolve, fetch, and integrate. Always use the .xcworkspace going forward — never open the .xcodeproj directly.
Whether to check in Pods/ depends on your team’s workflow. CocoaPods recommends keeping Pods/ under source control because a fresh clone can build without running pod install, but the trade-off is repo size and merge conflicts during updates.
Regardless of that choice, commit both Podfile and Podfile.lock. The lockfile records the resolved pod versions and is the minimum needed for reproducible installs.
Carthage took a decentralized approach: declare dependencies in a Cartfile, and Carthage builds them into frameworks or XCFrameworks that you drag into your project. It never modified your Xcode project or created a workspace.
Carthage still exists, but it has far less day-to-day adoption than SwiftPM. Existing projects can keep using it when it is stable for their build, but new projects should start with SwiftPM unless they have a specific binary-framework workflow that Carthage handles better.
Cartfile and Cartfile.resolved.Carthage/ from your repo.| Manager | Recommendation |
|---|---|
| SwiftPM | Default for all new projects. Integrated into Xcode, actively maintained by Apple, and supported by virtually every modern library. |
| CocoaPods | Use only if a required library is unavailable via SwiftPM. Plan to migrate when possible. |
| Carthage | Avoid for most new projects. Keep it for existing projects only when the current workflow is stable or you need its framework-oriented build style. |