Not Cuckoo for CocoaPods
The projects we work on generally have have dependencies on one or more (often several) open source projects, including our own libraries. We’ve always managed those dependencies using git submodules. As submodules have some quirks, a few projects have cropped up trying to make dependency management easier. The one that’s gained the most traction is CocoaPods.
Recently one of the projects we work on moved from submodules to CocoaPods. While CocoaPods has some nice aspects, I’m not crazy about the move. Here are some of the challenges I’ve faced.
Pods lack git history
If your dependencies are in git submodules and you run across a strange bit of code, you can easily look at the git history to see how the code was introduced. That information is often very helpful in determining the intention behind a piece of code, which helps you decide how to fix it.
With CocoaPods, when you do a pod install
, a snapshot of the dependency is installed in your project, and the dependency’s history is not available. You can always find the history by tracking down the project wherever it’s hosted, but it’s much less convenient and it’s easy to lose the context of how that dependency is used in your project.
Can’t modify Pods in context
When you run into a bug in a dependency project, often the easiest way to fix it is to modify the dependency in place and test it integrated with your code. If the dependency is in a submodule, it’s easy to make a change directly, push it to the origin repo (generally your own fork), and update the submodule pointer.
With CocoaPods, you have 2 choices:
- Make the change in a separate checkout of the dependency, push it to your fork, update the podspec, update the pod, test the integration, hope it worked.
- Make the change in place, test the integration, copy your edits to a checkout of the dependency, push it to your fork, update the podspec, update the pod, retest the integration to make sure you copied the right changes.
Neither of these is a desirable workflow, and both are error-prone.
To commit or not to commit
You can use CocoaPods a couple of different ways. One option is to install pods on each checkout of your project with pod install
and update them on each checkout with pod update
when the Podfile changes. Unfortunately, different checkouts can end up with different versions of the dependencies, which can lead to integration problems.
To avoid that risk, some projects keep the source of installed pods checked into their own projects. Then whenever a pod update is necessary, the update happens once, is committed, and all checkouts have consistent code. There is considerable debate in the CocoaPods community as to which approach is better.
The project we’re working on takes the latter approach, committing installed pods to its own repo. This presents a couple of disadvantages:
Repository size
If the entire content of a library is checked into your git repository, it grows along with the size of its dependencies. This makes every checkout of your git repository bigger and slower. The way git is structured, that growth remains even if you delete the dependency.
Modifying generated code
If the entire content of a library is checked into your repository, it’s easy to inadvertently commit changes to it. Let’s say you discover a bug in one of your dependencies. You make the change, test it, and commit it, not realizing that your fix will be overwritten the next time someone runs pod install
and commits the updated pods from the source. Even if you are a careful committer, it’s easy to inadvertently commit changes locally when generated code is checked into a repo.
The bottom line
Git submodules can be challenging to work with, which is what led to the development of CocoPods and other dependency managers. And CocoaPods does a great job of resolving the very problems people stumble with when using submodules. But that benefit comes at a cost of making it more difficult to modify and contribute back to open source projects.
If you don’t often make changes to the libraries you depend on, and don’t depend on your own shared libraries, CocoaPods may work very well for you. But if you actively improve and contribute to the libraries you use, submodules may still be the better choice. That’s what we’re sticking with for now.
7 Comments
Eloy Durán
December 10, 2013Hey Christopher,
I’m going to reply to some of your points in an inline fashion, I hope the formatting will be readable 🙂
> Can’t modify Pods in context
There are actually two more approaches:
* Add a category with the required changes to your project, then afterwards decide wether or not this really needs to go in the upstream lib and create a patch.
* Use the :path option, as described in the Using the files from a local path section, to use a pod from a local checkout, without having to commit any changes to the repo first.
> To commit or not to commit
There simply isn’t one solution to all. Whatever you choose depends mainly on the project you are working on. Do you do consultancy or is it your own product? Do other people need to be able to quickly build/run? These tend to be the decisive factors. I.e. in some cases you just want to have a checkout that will work regardless of the user having CocoaPods installed.
> Git submodules can be challenging to work with, which is what led to the development of CocoPods and other dependency managers.
CocoaPods is not about downloading code at all, it just happens to be a convenient feature. CocoaPods is about integrating dependencies (think of all the required Xcode build settings and especially when you are removing/updating a dependency) and making them work together so that the ecosystem becomes a more integrated system instead of everyone on their own islands.
Having said that, git-submodules never help if you need to integrate source from a non-git remote, whereas CocoaPods is agnostic.
Christopher Pickslay
December 10, 2013Interesting points Eloy, and useful to know. Thanks for commenting!
Jasper Blues
December 10, 2013I love CocoaPods!
Lots of reasons. Here’s just two:
* Makes it really fast to do continuous integration and upgrade dependencies
* Usually takes the headache out of settings transitive dependencies configs (ie linking in the frameworks and static libraries that your library depends on).
Let’s say you update a library and the new version requires an additional framework – CocoaPods takes care of this.
Another Feature That I like:
* CoocaPods can suppress warnings from library projects from propagating up and making noise in your main project.
There’s a lot of useful libraries out there that are stable and get the job done. But some of them have quite a few warnings.
For our own projects, our policy is, if there’s a way to provide the same feature without triggering a compiler warning (and there almost always is) then we’ll use that.
This way if the compiler triggers something sus’ we see it right away.
This all saves time and eliminates waste – which means more value to the end customer.
Yes, CocoaPods still has a few quirks, and it does not allieviate needing to know how things work under the hood in the event of a problem. . . I think ultimately though its still very useful and recommend you give it a try again.
Then, if you find something that you think could be done better, I know the team is very agile and eager to quickly incorporate improvements.
Give it another chance.
Christopher Pickslay
December 10, 2013I agree that you should not have warnings in your project, including dependencies. We always reference our own forks of open source libraries, so we fix the warnings in our own forks and send those fixes as pull requests to the source projects.
Jasper Blues
December 10, 2013Forgot to mention another feature that I like:
AppCode has create CocoaPods integration allowing you to do completion of library names, link through to the source, etc, check your Pod file parses, stc.
And detects also detects if you’ve edited the file and installs any new / removed stuff for you.
I think there’s a plugin for Xcode that does the same.
(OK – will let everyone else have their turn now 😉 )
Richard
April 10, 2014The single winning factor in my opinion is that it makes integrating 3rd party frameworks trivial in your project. Which means you can drop things in (and out!) and experiment while keeping them sandboxed from your main project.
I’d advocate checking in all project dependencies, so that you are taking an atomic snapshot with each commit. The benefits of this outweigh the disadvantages of filesize/bandwidth (these are computer problems, not human problems).
I’ve found the strategy of drafting patches via categories works very well. Getting into the mentality of working on several projects at once is a little tricky, but ultimately keeping encapsulation at the forefront of everything you do I think is a good thing. It makes for cleaner code, it makes for more streamlined development.
Cocoapods gotcha
October 8, 2014[…] Read more about the pros and cons Not Cuckoo for CocoaPods […]