Carthage or CocoaPods: That is the question
An every mature and modern programming languages come with an official solution for code distribution to share and reuse code that has been already written. The mechanism to share, distribute and re-use the code is usually handled by the package manager. The examples of popular package managers are RubyGems for Ruby, Composer for PHP, NPM for NodeJS, Gradle for Java etc, which not only provides official solutions but also has centralised repositories to host packages created by developers. However, Apple is an exception to this, the languages like Objective-C and Swift has been released to the public without having officially supported package management solution or having centralised repositories to host packages. This gives an opportunity for the third-party companies or developers to build an unofficial tooling to manage the packages for the Objective-C or Swift. That’s the reason the tools like Cocoapods and Carthage hits the market and gains popularity.
The Swift dependency management for iOS currently has only two main options at the moment which are 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, it’s development is being a speed turtle and it has no support for the iOS at the moment so it’s not worth thinking about it for now. There is a workaround to get it working with Xcode template for iOS projects but it might break with the later version of Xcode. Its good idea to be patient and wait till Apple announce official support of Swift Package Manager for iOS apps. We can hope that one day, iOS developers will have official package management solution for iOS from Apple.
Love it or hate it, you have to use it. Cocoapods exists since the old Objective-C days and works with Swift as well. This is mostly used dependency manage so far for the iOS project which is the de facto standard tool. CocoaPods comes as a Ruby library and needs to be installed using RubyGem.CocoaPods has the centralised repository of all packages that can be used in Xcode project. CocoaPods is not a single project but collection of projects written using Ruby. I have briefly described the history of the Ruby and 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 Ruby development background then you can use RVM and Bundler for managing versions of Ruby and Ruby libraries. CocoaPods can be initialised with pod init command which will create template Podfile but we can create our own simple Podfile will look like this
platform :ios, '8.0'
target 'MyApp' do
pod 'SwiftyJSON', '~> 2.3'
This will setup SwiftyJSON which is the very popular Swift library for parsing JSON for the My App target. Now, we can download dependency using the magical command
$ pod install
After this command, you will be confused and may not recognise 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 bothered to know what has been changed as long as iOS app compiles and builds properly after pod install command. We will briefly see what has changed after installation.
What Changes CocoaPods Made?
The above command (pod install) is very magical which make lots of changes to our Xcode project under the hood. The most of the times, it’s really hard to understand that what has been changed.
- CocoaPods creates Xcode Workspace directory, which has .xcworkspace extension where it builds all the dependency. 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
- Added Podfile.lock (Locked versions of libraries)
- Linked the Pods Frameworks to “Link Binaries with Libraries”
- A Pods directory containing source code of the Pod dependencies, supporting files, xcconfigfiles
- Lots of things inside your Xcode build Settings. It’s hard to cover everything here.
All these things mentioned above any more happens under the hood when you run the magical pod installcommand.
CocoaPods Build Process
The typical build process that CocoaPods projects include following steps.
- CocoaPods will build and compile our frameworks every time whenever you are doing the 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 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 adopted 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 setting and directory structure. As you have seen that integrating CocoaPods takes a minute but remove from the project is yak shaving. The CocoaPods has pod deintegrate and pod clean commands but still, we need to make sure what it has done. However, there some manual action to get rid of CocoaPods from iOS projects completely.
- Delete the standalone files ( Podfile Podfile.lock and your Pods directory)
- Delete the generated xcworkspace
- Open your xcodeproj file, delete the references to Pods.xcconfig and libPods.a (in the Frameworks group)
- Under your Build Phases delete the Copy Pods Resources , Embed Pods Frameworks and Check Pods Manifest.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 setup
- CocoaPods automate the entire process of building, linking the dependency to targets.
- The CocoaPods community has done an impressive job to fix Apple’s shortcomings
- CocoaPods supports libraries having subspec.
- Lots of contributors with well-grown community
- Works well for the 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 controls of entire Xcode project and if something fails entire project stops building. The fixing the errors thrown by CocoaPods are the 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 Continuous Integration Server is hard as we have to install and manage Ruby libraries. All the dependency needs to be installed and built for every build Or we have to check in the entire third-party dependencies inside the project. Caching mechanism doesn’t always give clean results.
- The building of iOS app became an intransparent and slow process.
- Building libraries and frameworks that support CocoaPods became such a pain for iOS developers who are not skilled in Ruby. The developer needs to write .podspec file and follow many unrelated Ruby conventions.
- Can’t work with framework and project at the same time because of the two-step process for working on dependencies.
Carthage is another simple dependency manager for the Cocoa application. 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 exist when CocoaPods is there, you can read this differences here. Carthage just downloads and build the dependencies using xcodebuild tool but will not change Project file Or Xcode project build setting like CocoaPods. We have to manually drag framework binaries to Linked Frameworks and Libraries inside build phase of the target.
$ brew install carthage
We are now ready to use Carthage. As above, we need to get SwiftyJSON using Carthage then we have to creat file called Cartfile with the following content.
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 it 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 the new dependency.
What Changes Carthage Made?
Carthage won’t touch Xcode settings or Project files. Carthage is very simple and just check out and build the dependencies and leave 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 that Carthage projects include following steps.
- Carthage retrieves the libraries and framework using carthage update command which happens only once.
- Xcode will not rebuild any framework when building the project. This speeds up the build process.
- The framework needs to rebuild wh we get the new version of any dependency.
How to Remove Carthage?
Once integrated, it’s very easy to remove Carthage from iOS project. There are few steps that we can take to make sure Carthage is completely removed from Xcode project.
- Delete Cartfile and Cartfile.resolved
- Delete Carthage directory
- Remove the linked frameworks from target build phase.
- Remove the copy-frameworks script from build phase.
That’s it. you have no longer using Carthage.
Benefits of Carthage
- Carthage is decentralised, 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 project needs.
- Carthage won’t touch your Xcode settings or project files. It just downloads and builds the dependencies so you have proper control on what you are doing.
- Carthage works great for the large or eclectic codebases because of its flexibility
- Building and updating lib(s) are easier with Carthage.
- Carthage can be easily integrated with Continuous Integration server.
- Making Swift libraries Carthage compatible is easy.
- Supports submodules
Downsides of Carthage
- Carthage has too many manual steps that need to be performed while setup.
- Carthage is still new framework and not 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 checkout and builds the frameworks, we might need to check-in those frameworks for faster builds which adds repository size.
- Some users reported Carthage is slow while building frameworks.
- Carthage installed with homebrew is not backward compatible.
- Not all the frameworks are available for Carthage especially older libraries that need to be added using git submodules.
Carthage is very simple Swift dependency manager but it involves a lot of manual setups to get started.
Tips for Selecting Carthage Or CocoaPods
Now that, 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 nothing wrong with Carthage or CocoaPods but choosing the right one really depends on the team and skills of the engineers working in 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 10 times before adding any dependency to your project. e.g When making network calls, do you really need Alamofire, whats missing that Apple’s native URLSession cannot solve your problem. Try to achieve everything with native frameworks as there are rare chances that Apple will break your project.
- While starting new projects, think 100 times before using CocoaPods. When you started 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 the large code base, use Carthage as it needs to be built only once.
- If you are thinking to use many libraries in your project CocoaPods won’t be a good choice as they will build the framework everytime you do the 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 the single project as it makes sense 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 Continuous Integration server.
- Libraries using multiple repos can be well handled with CocoaPods subspec while there is no clean solution for Carthage.
- Whatever you select, always check-in the source code of the third-party libraries in the SCM repositories. Never risk your project when 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 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 something you feel misleading in this post. What are your experiences using Carthage or Cocoapods, share in the comments below?