Carthage or CocoaPods: That Is the Question
Learn about CocoaPods and Carthage, the two main dependency management tools for iOS, and how they work to choose the right one for you.
Join the DZone community and get the full member experience.
Join For FreeEvery mature and modern programming language comes with an official solution for code distribution, to share and reuse code that has been already written. The mechanism to share, distribute, and reuse code is usually handled by the package manager. Examples of popular package managers are RubyGems for Ruby, Composer for PHP, NPM for NodeJS, and Gradle for Java, which not only provides official solutions but also has centralized repositories to host packages created by developers. However, Apple is an exception to this; languages like Objective-C and Swift were released to the public without an officially supported package management solution or centralized repositories to host packages. This gives an opportunity for third-party companies or developers to build unofficial tooling to manage packages for Objective-C or Swift. That's the reason tools like CocoaPods and Carthage hit the market and gain popularity.
Swift dependency management for iOS has only two main options at the moment: CocoaPods and Carthage. The official package manager that Apple is currently working on is Swift Package Manager, to share and distribute Swift packages, which will be a replacement for the current package managers in the iOS development world. However, its development is being a turtle and it has no support for iOS at the moment, so it's not worth thinking about it for now. There is a workaround to get it working with Xcode templates for iOS projects, but it might break with a later version of Xcode. It's a good idea to be patient and wait until Apple announces official support of Swift Package Manager for iOS apps. We can hope that one day, iOS developers will have an official package management solution for iOS from Apple.
In this post, we will critically evaluate Swift dependency managers CocoaPods and Carthage so that iOS developers can make the right choice for their apps.
CocoaPods
Love it or hate it, you have to use it. CocoaPods has existed since the old Objective-C days and works with Swift as well. This is the most-used dependency manager so far for iOS projects and is the de facto standard tool. CocoaPods comes as a Ruby library and needs to be installed using RubyGem. CocoaPods has a centralized repository of all packages that can be used in Xcode projects. CocoaPods is not a single project, but a collection of projects written using Ruby. I have briefly described the history of the Ruby and the iOS ecosystem here. CocoaPods is built with Ruby and is installable with the default Ruby available on OS X:
$ sudo gem install cocoapods
If you are from a Ruby development background, then you can use RVM and Bundler to manage versions of Ruby and Ruby libraries. CocoaPods can be initialized with the pod init
command, which will create a template Podfile
, but we can create our own simple Podfile
, which will look like this:
platform :ios, '8.0'
use_frameworks!
target 'MyApp' do
pod 'SwiftyJSON', '~> 2.3'
end
This will set up SwiftyJSON, which is a very popular Swift library for parsing JSON for the My App target. Now, we can download a dependency using the magical command:
$ pod install
After this command, you will be confused and may not recognize your old Xcode project, as CocoaPods has done a lot of changes to your Xcode project with this magical command. I am sure not many of us bother to know what has been changed as long as the iOS app compiles and builds properly after the pod install
command. We will briefly see what has changed after installation.
What Changes Did CocoaPods Make?
The above command (pod install) is very magical and makes lots of changes to our Xcode project under the hood. Most of the time, it's really hard to understand that what has been changed.
- CocoaPods creates the Xcode Workspace directory, which has a
.xcworkspace
extension where it builds all the dependencies. We have to use Xcode Workspace in order to make CocoaPods work. - CocoaPods added some scripts in the build phases of our target. There are usually three scripts added to the build phases of the target:
- It added Podfile.lock (locked versions of libraries).
- It linked the Pods Frameworks to "Link Binaries with Libraries."
- A Pods directory containing source code of the Pod dependencies, supporting files, and
xconfig
files. - Lots of things inside your Xcode build Settings. It’s hard to cover everything here.
All these things mentioned above and more happen under the hood when you run the magical pod install
command.
CocoaPods Build Process
The typical build process for CocoaPods projects includes the following steps:
- CocoaPods will build and compile our frameworks every time you are doing a clean build, or run pod install or pod update for the project.
- Xcode then checks if
Podfile.lock
has changed. If that changed, then Xcode will build the dependency framework again; otherwise, it uses the pre-built frameworks. - You might have seen that project taking a long time when you do the clean build or delete derived data when using CocoaPods.
- CocoaPods will build all the libraries mentioned in the Podfile for that target, there might be some cases where you don't want to build some libraries all the time but CocoaPods won't have that option.
How to Remove CocoaPods
Once you adopt CocoaPods, there is no clean way to de-integrate it from your project. CocoaPods has made lots of changes all over your Xcode project, build settings, and directory structure. As you have seen, integrating CocoaPods takes a minute, but removing it from the project is yak shaving. CocoaPods has the pod deintegrate
and pod clean
commands, but still, we need to make sure of what it has done. However, there are some manual actions to get rid of CocoaPods from iOS projects completely.
- Delete the standalone files (
Podfile
,Podfile.lock
, and yourPods
directory). - Delete the generated
xcworkspace
. - Open your
xcodeproj
file and delete the references toPods.xcconfig
andlibPods.a
(in theFrameworks
group). - Under your
Build Phases
, delete theCopy Pods Resources
,Embed Pods Frameworks
, andCheck PodsManifest.lock
phases. - This may seem obvious, but you’ll need to integrate the 3rd party libraries some other way or remove references to them from your code.
Benefits of CocoaPods
- CocoaPods is easy to set up.
- CocoaPods automates the entire process of building and linking the dependencies to targets.
- The CocoaPods community has done an impressive job of fixing Apple's shortcomings.
- CocoaPods supports libraries having subspec.
- Lots of contributors with a well-grown community.
- Works well for small projects with less code to get something working very quickly.
Downsides of CocoaPods
- It requires knowledge of another programming language, i.e. Ruby, on which CocoaPods is built.
- Hard to integrate when already using Xcode Workspace.
- Hard to remove once integrated.
- CocoaPods takes control of the entire Xcode project, and if something fails, the entire project stops building. Fixing the errors thrown by CocoaPods is a hard and time-consuming task and requires an understanding of what CocoaPods changed inside the iOS project.
- Pods force your project into a specific structure. CocoaPods updating Xcode projects and files is like magic without understanding what's changed. It works in a black box way.
- Integrating with a Continuous Integration server is hard as we have to install and manage Ruby libraries. All the dependencies need to be installed and built for every build, or we have to check in all the third-party dependencies inside the project. The caching mechanism doesn't always give clean results.
- The building of iOS apps becomes an intransparent and slow process.
- Building libraries and frameworks that support CocoaPods is such a pain for iOS developers who are not skilled in Ruby. The developer needs to write a
.podspec
file and follow many unrelated Ruby conventions. - Centralized.
- Can't work with a framework and project at the same time because of the two-step process for working on dependencies.
Carthage
Carthage is another simple dependency manager for iOS applications. Carthage has been written purely in Swift to manage iOS dependencies without changing anything inside your Xcode projects. If you want to know why Carthage exists when CocoaPods is there, you can read about the differences here. Carthage just downloads and builds the dependencies using the xcodebuild
tool but will not change the project file or Xcode project build settings like CocoaPods. We have to manually drag framework binaries to Linked Frameworks and Libraries
in the build phase of the target.
We can install Carthage using HomeBrew or you can download the .pkg from GitHub and install it manually:
$ brew install carthage
We are now ready to use Carthage. As above, we need to get SwiftyJSON using Carthage, then we have to create a file called Cartfile with the following content:
github "SwiftyJSON/SwiftyJSON"
Now that we have specified our dependency in the Cartfile, run $ carthage update
. This will fetch dependencies into a Carthage/Checkouts folder, then build each one. Now everything CocoaPods does automatically as magic, we have to do manually.
On your application targets General settings tab, in the Linked Frameworks and Libraries section, drag and drop each framework you want to use from the Carthage/Build folder on disk. After doing this all manual work, we should be able to import dependencies. However, the manual work mentioned above is one time, or after adding a new dependency.
What Changes Did Carthage Make?
Carthage won't touch Xcode settings or Project files. Carthage is very simple and just checks out and builds the dependencies and leaves it to you to add the binaries to Xcode. It gives you full control of what we are adding to Xcode.
Carthage Build Process
The typical build process for Carthage projects includes the following steps:
- Carthage retrieves the libraries and framework using the
carthage update
command, which happens only once. - Xcode will not rebuild any frameworks when building the project. This speeds up the build process.
- The framework needs to rebuild when we get a new version of any dependency.
How to Remove Carthage
Once integrated, it's very easy to remove Carthage from an iOS project. There are a few steps that we can take to make sure Carthage is completely removed from the Xcode project:
- Delete Cartfile and Cartfile.resolved.
- Delete the Carthage directory.
- Remove the linked frameworks from the target build phase.
- Remove the copy-frameworks script from the build phase.
That's it. You are no longer using Carthage.
Benefits of Carthage
- Carthage is decentralized, simple and ruthless dependency management for iOS.
- Carthage is purely written in Swift, so iOS developers can understand the technology behind Carthage and probably write another tool using CarthageKit.
- Carthage is easy to integrate and easy to remove from the project if it doesn't suit the project's needs.
- Carthage won't touch your Xcode settings or project files. It just downloads and builds the dependencies so you have proper control of what you are doing.
- Carthage works great for large or eclectic codebases because of its flexibility.
- Building and updating lib(s) is easier with Carthage.
- Carthage can be easily integrated with a Continuous Integration server.
- Making Swift libraries Carthage compatible is easy.
- Decentralized.
- Supports submodules.
Downsides of Carthage
- Carthage has too many manual steps that need to be performed during setup.
- Carthage is still a new framework and does not have as many contributors as CocoaPods.
- Carthage only works with dynamic frameworks. It doesn't work with static libraries.
- Carthage has no clean way to support subspec within libraries.
- Traditional iOS developers who came from Objective-C backgrounds feel reluctant to use Carthage.
- Carthage checks out and builds the frameworks; we might need to check-in those frameworks for faster builds, which adds repository size.
- Some users report that Carthage is slow while building frameworks.
- Carthage installed with Homebrew is not backward-compatible.
- Not all frameworks are available for Carthage, especially older libraries that need to be added using Git submodules.
Carthage is a very simple Swift dependency manager, but it involves a lot of manual setups to get started.
Tips for Selecting Carthage or CocoaPods
Now we have seen the pros and cons of both CocoaPods and Carthage, but the question is, which one to pick for your iOS project. I hate this answer, but the answer is "It depends." There is nothing wrong with Carthage or CocoaPods, but choosing the right one really depends on the team and skills of the engineers working on the team.
Here are some tips:
- First of all, try to avoid adding any dependency to your iOS project unless it's really, really required. Apple has given you lots of native frameworks and tools. There are also possibilities to add extra functionality to the native frameworks provided by Apple. Think ten times before adding any dependency to your project. For example, when making network calls, do you really need Alamofire? What's missing that Apple's native URLSession cannot solve your problem? Try to achieve everything with native frameworks, as it is rare that Apple will break your project.
- While starting new projects, think 100 times before using CocoaPods. Once you start using CocoaPods, it's very hard to remove it from your Xcode project and you will lose control over your Xcode project.
- Prefer Carthage first, as it's written in Swift, and if you think it's not suitable, then go for CocoaPods and deal with Ruby.
- While integrating libraries with a large code base, use Carthage, as it needs to be built only once.
- If you are thinking of using many libraries in your project, CocoaPods won't be a good choice as it will build the framework every time you do a clean build. Also, as mentioned earlier, you don't have the chance to stop building libraries that you don't update often. This adds extra time to your build process.
- Don't be afraid to use both CocoaPods and Carthage in a single project as it makes sense, as in many situations as mentioned above. We can take advantage of the features of both CocoaPods and Carthage at the same time.
- If you are not skilled in Ruby, then definitely avoid CocoaPods. It will become very hard to maintain the version of Ruby and Ruby libraries on the local machine as well as on the Continuous Integration server.
- Libraries using multiple repos can be handled well with CocoaPods subspec, while there is no clean solution for Carthage.
- Whatever you select, always check-in the source code of third-party libraries in the SCM repositories. Never risk your project when a library goes missing from GitHub.
- Use Carthage to check out the private or internal frameworks.
Both Carthage and CocoaPods have their pros and cons, but selecting one tool might be hard for any project. My personal preference is to go with Carthage first, then try to deal with Ruby and CocoaPods if Carthage fails. For many old Objective-C iOS projects where CocoaPods became the white elephant, it's time to make some changes and try out Carthage if your project is migrated to Swift. Feel free to correct me if you feel misleading is misleading in this post. What are your experiences using Carthage or CocoaPods? Share in the comments below.
Published at DZone with permission of Shashikant Jagtap, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments