CocoaPods is now the industry standard for managing third-party frameworks and dependencies for iOS. There’s such a demand to make libraries work with CocoaPods that, even in its very early days, I actually received 5 requests to add a Podfile and tag before I even knew what CocoaPods was! Nowadays, I love it, and I’d never even consider starting a project without it!
Let’s take a concise look at how CocoaPods works.
The One-Time Installation
CocoaPods is actually implemented as a Ruby gem, so assuming you have Ruby already installed, install the gem with:
gem install cocoapods
If you’re not familiar with Ruby, you might need sudo access to get this command to work.
Setting Up Your Project
Setting up CocoaPods for your project doesn’t get much harder. First, make sure you’ve created your XCode project if you haven’t already. (I’ll wait.) Great! Now, you need to create an initial Podfile. At the top of your project hierarchy, in the same directory as your project’s .xcodeproj file, run:
This creates the Podfile for you. Now open it and add the following:
Next, install the CocoaPods and framework and your new dependency with:
Magic be done! Now take another look at your directory contents. CocoaPods has created a complementary .xcworkspace file that you should use from now on to open your project. When you do, you’ll find that your project now shares the workspace with CocoaPods and all your dependencies are managed for you!
Pod Selection and Specification
If you specified the Podfile as listed above, you’ll have the latest version of RaptureXML in your project. It’s very easy to add other libraries and be even more specific as to what version you want. Let’s say you want to add AFNetworking to your project…what do you do? I always start with a search:
pod search AFNetworking
A few items comes up, but it’s quick to see that this is what you’re looking for:
-> AFNetworking (2.0.0) A delightful iOS and OS X networking framework. pod 'AFNetworking', '~> 2.0.0' ...
Even better, the third line there is CocoaPods telling you exactly what you need to add to your Podfile to install AFNetworking! Yet, what’s that syntax? It’s a little weird, so here’s some examples to give you an idea:
pod 'AFNetworking', '~> 2.0.0' <-- Installs latest version up to, but not including, 2.1 pod 'AFNetworking', '~> 2.0' <-- Installs latest version up to, but not including, 2.9 pod 'RaptureXML', '1.0.1' <-- Installs version 1.0.1 pod 'iRate' <-- Installs most recent version
There’s other options in terms of versioning, but I don’t find anything else very useful. Most of the time, I just grab the latest version. There’s some risk there in case a bug is introduced into a dependency, so if it’s critical for testing that things stay the same, you might opt to lock the versions down in the release branch version of your Podfile. Stick to using the latest versions in your main branch, though, because you don’t want to become dependent on an old version of anything.
This is my favorite feature of CocoaPods. Previously, we’ve been installing pods from CocoaPods’ central repository, but there’s no requirement that you have to. We can alternatively load our pods directly from any Git repository:
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git'
And, my favorite, you can point directly at a path on your own machine!
pod 'AFNetworking', :path => '~/Rapture/Tilikum'
This is just brilliant because, in both of the above cases, you can manage your own pods privately without exposing them to the central repository that allows anyone access. This is great when you, or your company, have internal libraries that you want to manage with CocoaPods but don’t want anyone else using. I use this endlessly to capture re-usable code that I use repeatedly in many of my own projects.
Creating the Pod
So, how do the pods know how to configure themselves for your app? This takes a little more work, but it’s not too bad. First, you’ll likely need to re-configure your library or framework’s folder hierarchy just a little. Here are a couple tips:
- Centralize the re-usable code in your library under one directory hierarchy (i.e., Classes/ or Tilikum/).
- Separate app delegates, Info.plist, and any other files which shouldn’t be included in a host app. (So they’re not under Classes/)
Now, in your library, create your podspec at your project’s top level. Let’s pretend we’re creating a library of avatar masks. We run:
cd ~/Projects/AvatarMasker pod spec create AvatarMasker
This will create an AvatarMasker.podspec in that directory. There’s a lot of comments in that file to help you configure it. My best advice is to browse real-world podspecs to see what they do. Here are the podspecs for RaptureXML, iRate, and ZipZap.
One thing you may notice while you’re browsing around is that some projects don’t appear to have podspecs, but are searchable in CocoaPods. This is because it’s not required that the podspec be in the repository itself. In these cases, the podspec is located only on the CocoaPods central repository. This is, for example, the case for InnerBand. If you want to add your library to the central repository, you’ll just need to upload your podspec to a different place.
Also, note that if the project doesn’t host its own podspec, you can’t load it directly like we just did above. You would have to load it from the central repository.
CocoaPods is clearly the superior option compared to adding each dependency to your project by hand. Every library seems to have unique requirements for how to integrate it and you’ll often include more than you need when you do it this way. You’ve seen that you can integrate a library as large as AFNetworking in a matter of seconds, so why would you want to do it yourself? In addition, you gain the power to specify any version you want without having to reconfigure every time — at will.
Sometimes, developers will use Git submodules to avoid copying library source code into the app. This is fine, but personally I despise submodules. Git just doesn’t implement them very elegantly and, inevitably, you’ll run into errors when a developer doesn’t update the submodule pointer properly or forgets to push or pull submodule commits. On top of all of this, you still have to do the library integration on your own.
If you’ve never used CocoaPods, I hope I’ve inspired you to try out one of the most valuable tools in the iOS toolbox!