robotics-and-intelligent-systems
How to Manage Dependencies with Cocoapods and Carthage in Ios Projects
Table of Contents
Introduction to Dependency Management in iOS
Modern iOS development rarely starts from scratch. Third-party libraries handle everything from networking and JSON parsing to UI components and image caching. Without a structured approach, manually downloading frameworks, resolving version conflicts, and linking binaries quickly becomes a maintenance nightmare. This is where dependency managers come in.
CocoaPods and Carthage are the two most established tools for managing dependencies in iOS projects. While CocoaPods offers a streamlined, workspace-based integration, Carthage follows a decentralized, build-it-yourself philosophy. Understanding the strengths and trade-offs of each helps you pick the right approach for your team, your app, and your deployment pipeline.
CocoaPods: Centralized and Automated
CocoaPods has been the dominant dependency manager since its introduction. It uses a central index called CocoaPods Specs and generates an Xcode workspace that handles all dependencies automatically. This means you can add a library by simply declaring it in a Podfile, run a command, and immediately start importing it in your code.
Installation and Setup
CocoaPods is installed via RubyGems. macOS ships with Ruby, but you may need to upgrade or use a Ruby version manager. The standard installation command is:
sudo gem install cocoapods
After installation, navigate to your project directory and initialize a Podfile:
pod init
This creates a plaintext file named Podfile. You then edit it to specify your target platform, any use_frameworks! flag (required for Swift libraries), and the dependencies you need. A typical Podfile looks like this:
platform :ios, '15.0'
target 'MyApp' do
use_frameworks!
pod 'Alamofire', '~> 5.7'
pod 'SwiftyJSON', '~> 5.0'
pod 'SDWebImage', '~> 5.15'
end
Once the Podfile is ready, run:
pod install
CocoaPods downloads the specified versions, resolves dependencies, and generates an .xcworkspace file. From that point on, you must open the workspace—not the original .xcodeproj—to build and run your app.
Advanced CocoaPods Features
- Subspecs: Many libraries allow you to import only a subset of their functionality. For example,
pod 'Alamofire/EventMonitor'reduces the binary footprint. - Local paths: You can point to a local folder for private libraries:
pod 'MyPrivateLib', :path => '../MyPrivateLib' - Git-based sources: Dependencies can come from any Git repository:
pod 'MyLib', :git => 'https://github.com/company/MyLib.git' - Podfile.lock: This file locks every dependency to a specific version, ensuring reproducible builds across your team and CI.
- Plugins: You can extend CocoaPods with plugins for SwiftLint, Firebase, or custom build scripts.
CocoaPods also supports multiplatform targets. You can have different dependency sets for iOS, macOS, and watchOS by nesting target blocks inside a single Podfile.
Post-Install Hooks and Customization
A common need is to run a script after every pod install. For example, you might want to strip simulator architectures from release builds. This is done with a post_install hook:
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
end
end
end
Such hooks give you fine-grained control over the generated Pods project, but they also add complexity. Overuse can make your Podfile hard to read and maintain.
Carthage: Decentralized and Hands-Off
Carthage takes a different approach. Instead of centralizing metadata, it relies on Git tags and the actual Xcode projects of each library. Carthage builds the frameworks on your machine and leaves the integration step—dragging frameworks into your Xcode project—entirely up to you. This minimal interference is appealing to developers who want full control and a smaller footprint in their version control.
Installation and Setup
Carthage is typically installed via Homebrew:
brew install carthage
Next, create a plaintext file named Cartfile in your project root. The syntax is similar to CocoaPods but points to GitHub repositories or any Git source:
github "Alamofire/Alamofire" ~> 5.7
github "SwiftyJSON/SwiftyJSON" ~> 5.0
github "onevcat/Kingfisher" ~> 7.0
After editing the Cartfile, run:
carthage update --platform iOS
This command clones the repositories, checks out the tagged versions, and builds the frameworks using Xcode. The resulting binaries are placed in the Carthage/Build/iOS folder. You then manually drag them into your Xcode project under the "Frameworks, Libraries, and Embedded Content" section, making sure to add them to the appropriate target.
Key Differences from CocoaPods
- No workspace: Carthage does not modify your project file. You stay in control of file references and build settings.
- Build speed: Carthage can cache builds. On CI, you can prebuild dependencies to speed up the pipeline.
- Versioning: Carthage uses a Cartfile.resolved to lock versions, similar to Podfile.lock.
- Binary frameworks: Some libraries ship
.xcframeworkbinaries. Carthage can download those directly, skipping the build step and saving time. - XCFramework support: Since Carthage 0.38, it can output XCFrameworks, which work seamlessly with Swift Package Manager and eliminate the need to strip simulator architectures.
Handling Frameworks with Dependencies
One challenge with Carthage is that it builds dependencies in sequence. If library A depends on library B, you must list both in the Cartfile. Carthage resolves the dependency tree automatically, but you still need to link all transitive dependencies in your project manually. This gives you visibility into every binary your app links against, but it also increases the chance of missing a required framework at runtime.
Comparing CocoaPods and Carthage: A Practical Guide
Choosing between the two depends on your project size, team maturity, and deployment workflow. The table below summarizes the key trade-offs.
| Factor | CocoaPods | Carthage |
|---|---|---|
| Setup complexity | Low – one command, workspace generated automatically. | Medium – requires manual linking of frameworks. |
| Build system control | Lower – CocoaPods merges project files and may override build settings. | Higher – you control project structure and build phases. |
| Integration with Xcode | Tight – workspace includes Pods project, all configurations preset. | Loose – you add frameworks manually; no workspace changes. |
| CI/CD compatibility | Good – pod install works reliably in CI, but full rebuild on each run if lockfile changes. |
Excellent – prebuilt frameworks can be cached; build times are faster. |
| Swift Package Manager migration | Can coexist but may cause conflicts if both manage the same library. | Can coexist more easily because Carthage does not modify project files. |
| Community and library availability | Widest coverage – almost every popular library has a CocoaPods spec. | Good coverage – but some niche libraries may not be Carthage-friendly. |
When to Use CocoaPods
- You are starting a new project and want minimal boilerplate.
- Your team includes junior developers who benefit from a hands-off integration.
- You need a library that is only available via CocoaPods (still happens for some legacy or proprietary pods).
- You heavily rely on CocoaPods plugins (e.g., for lint checks or code generation).
When to Use Carthage
- You value modularity and want to avoid the "Pods project" bloat.
- Your app is large, and you need to optimize build times by caching prebuilt frameworks.
- You are migrating to Swift Package Manager and want a gradual transition without breaking existing integrations.
- You work on a team that prefers to keep the Xcode project lean and manually manage build settings.
Migration Between Dependency Managers
Switching from CocoaPods to Carthage or vice versa is possible but requires careful planning. Here are the high-level steps.
Migrating from CocoaPods to Carthage
- Remove the Podfile, Podfile.lock, and the workspace.
- Remove any Pods-related build phases (e.g., “Embed Pods Frameworks”).
- Create a Cartfile and list the same libraries (ensuring they support Carthage).
- Run
carthage update --platform iOS. - Manually add each framework from Carthage/Build/iOS to the Xcode project.
- Update all import statements – under Carthage, you import frameworks directly (e.g.,
import Alamofire). - Test thoroughly; transitive dependencies may now need explicit linking.
Migrating from Carthage to CocoaPods
- Remove Carthage-related build phases and framework references from the Xcode project.
- Delete the Cartfile and Cartfile.resolved.
- Run
pod initto create a Podfile. - Add all dependencies with appropriate version constraints.
- Run
pod installand then open the new workspace. - Check for duplicate imports – CocoaPods may embed libraries differently.
- Update build settings if necessary (e.g.,
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES).
Best Practices for Both Managers
Regardless of which tool you choose, following these practices will keep your project healthy.
- Commit lock files: Always commit Podfile.lock or Cartfile.resolved to version control. This ensures every team member and CI server uses exactly the same versions.
- Pin versions carefully: Use optimistic operators (
~>) to allow minor updates while blocking major breaking changes. Specify exact versions only when you need absolute stability. - Run updates deliberately: Do not run
pod updateorcarthage updatewithout reviewing the changelogs of updated dependencies. Schedule version bumps with sprint planning. - Audit for Swift version compatibility: Some libraries may not support the Swift version your project uses. Check that the dependency’s branch or tag matches your Swift toolchain.
- Remove unused dependencies: Periodically review your Cartfile or Podfile and remove libraries that are no longer used. Orphaned dependencies bloat the binary and increase attack surface.
- Consider SPM for new projects: Swift Package Manager is now built into Xcode and supported by most major libraries. If you are starting from scratch, SPM may be the simplest choice. Both CocoaPods and Carthage remain excellent for managing large legacy projects or private frameworks.
Troubleshooting Common Issues
CocoaPods: “Spec not found”
This usually means the library has not been pushed to the CocoaPods trunk or you are using a wrong name. Verify the pod name on cocoapods.org. If the library is private, you need to specify its source in your Podfile.
CocoaPods: Conflicts in transitive dependencies
Run pod update --no-repo-update and inspect the output. You may need to add explicit version constraints for transitive pods. Using pod deintegrate and a fresh pod install can reset the dependency graph.
Carthage: “No such module” when building
Often this happens because the framework was not built for the correct platform (e.g., you built with --platform macOS by mistake). Re-run carthage update --platform iOS and verify the output folder. Also ensure that you added the framework to the “Frameworks, Libraries, and Embedded Content” section, not just the project navigator.
Carthage: Build fails because of missing dependencies
If a library you are using has its own dependencies (like RxSwift dependencies), you must list them in your Cartfile. Carthage does not automatically download transitive dependencies unless they appear in the Cartfile or are specified as submodules.
Hybrid Approaches: Using Both CocoaPods and Carthage
While mixing dependency managers in a single project is not recommended, some teams do it out of necessity. For example, a critical library might only be available via CocoaPods, while the rest of the project uses Carthage. If you must combine them, keep the CocoaPods workspace separate and link Carthage frameworks manually. Be aware of potential conflicts in duplicate symbols or overlapping resources. The simpler solution is usually to choose one manager and migrate any libraries that are not supported.
The Future of iOS Dependency Management
Swift Package Manager (SPM) is now considered the standard by Apple and is integrated directly into Xcode 11 and later. Most open-source libraries have added SPM support, and SPM eliminates the need for external tools. However, both CocoaPods and Carthage still have advantages:
- CocoaPods offers rich customization through hooks and plugins, and its spec repository remains the largest collection of iOS libraries.
- Carthage gives you full control over the build process and is easier to cache, making it popular in CI-heavy workflows.
Many teams use SPM for new dependencies while maintaining legacy integrations with CocoaPods or Carthage. Over time, SPM is expected to become the default, but for now, understanding all three tools enables you to work on any iOS codebase.
Conclusion
Effective dependency management is a cornerstone of professional iOS development. CocoaPods provides a turnkey solution that automates the entire integration process, making it ideal for teams that want speed and simplicity. Carthage offers a leaner, more transparent approach that gives developers granular control over build systems and project structure. By mastering both tools, you can choose the right fit for your project’s size, complexity, and workflow. Whichever you pick, always commit lock files, update dependencies deliberately, and keep an eye on the evolving landscape of Swift Package Manager.
For further reading, explore the official CocoaPods guides and the Carthage GitHub repository.