iOS Development: Pushing CocoaPods to the Limit

There is a magical tool that I have loved to use ever since I started my iOS development journey, and that is CocoaPods – an extremely easy-to-use tool that allows iOS developers to pull in the libraries and dependencies they need so that they can quickly focus on their own projects. Needless to say, I love CocoaPods; I definitely saw the potential for the speed and agility a tool like this could offer any iOS development team.

For the last year or so, I have been trying to leverage CocoaPods in the greater context of Continuous Integration systems. Below are tips and tricks that I want to show other iOS developers out there that are not readily evident in the CocoaPods documentation, but that have been extremely beneficial to the speed and elegance that we all strive for in our work. Below are some ways that I have found that really push CocoaPods to its limits.

Take Advantage of Multiple Targets

An extremely powerful feature of CocoaPods is to give the user the ability to define exclusive Pod dependencies specific to a target. In my development, I sometimes find myself needing to add libraries that should really only exist in my development builds or in my automation tests or unit tests – dependencies that have no need to be linked in my build phases. To do this with CocoaPods, I would do the following:

# Specific Dependencies for my Development builds
target 'MyDevTarget', :exclusive => true do
      pod 'Reveal-iOS-SDK', '~> 1.0.4'
end

As can be seen above, the Reveal iOS SDK will be downloaded, configured, and linked exclusively with MyDevTarget. Note that the impetus behind me wanting to link the Reveal iOS SDK (a powerful view inspection tool) only with this target is that it should not be bundled with an App Store build, but is still a useful library to include when debugging in-house builds. 

Another instance illustrating the usefulness of this feature is with my test targets: 

# Specific Dependencies for my Unit Tests
target ‘MyUnitTestTarget', :exclusive => true do
    pod 'OCMock', '~> 2.2.4'
    pod 'Kiwi', '~> 2.2.4'
end

# Specific Dependencies for my UI Tests
target 'MyUITestTarget', :exclusive => true do
    pod 'KIF', '~> 3.0.3'
end

Now my Podfile is equipped with the ability to add specific libraries pertinent to these test targets. As illustrated, OCMock, a great implementation of mock objects, is added only to my unit test target as it is not needed in my development, beta, or production builds. Similarly, KiF, a great UI acceptance testing project, really only needs to be added when I run my UI tests. 

Taking advantage of the above syntax will give great expressive power to our Podfile, and will really push CocoaPods to the limit to work with our Continuous Integration system. With it, we can ensure that extraneous dependencies do not seep into our different builds. If you have not done so yet, it is recommended these development targets be created in your Xcode project to start taking full advantage of this feature in CocoaPods. 

Define Common Pods Across Multiple Targets

Let’s continue with the situation above where we now have multiple targets. such as MyDevTarget and MyProdTarget. Given that we have these separate targets, how do we define common dependencies that should be included in every build? Simple, by taking advantage of the fact that Podfiles are written in Ruby underneath, like this: 

# Define common pods in this function
def myCommonPods
    pod ‘AFNetworking’, , '~> 2.3.1’
    pod ‘CocoaLumberjack’, , '~> 1.9.0'
    pod ‘CustomUIActionSheet’, '~> 0.1.1'
end

target 'MyDevTarget', :exclusive => true do
    # Define specific pods to the target here
    pod 'Reveal-iOS-SDK', '~> 1.0.4’
    # Define common pods here by calling our new method above
    myCommonPods
end

target 'MyProdTarget', :exclusive => true do
    # Define common pods here by calling our new method above
    myCommonPods
end

The example above shows how simple it is to express shared libraries we want to link across targets in an extremely eloquent way. Here we take advantage of a little Ruby and define a function that lists out the commonalities for us. The advantage of this method is in its simplicity: we do not have to repeat ourselves in our Podfile – underneath the hood, we leverage the fact that CocoaPods will generate the config files, prefix headers, and linkers for us automatically.

Miscellaneous Tips: warnings, post install hooks, branches, and more

The following are just a few miscellaneous tricks I have found useful during development: 

Use inhibit_all_warnings! to suppress all warnings that may come from the pods that you pull into your project

Use the post_install hook to run code that you need to be executed after all the pods have been installed:

post_install do | installer | … end

 

There is no need to support multiple podfiles across your branches (development, integration, etc…). You can use the magic of Ruby to detect what branch your project is currently on and tell CocoaPods to pull the appropriate pod dependency, all in one Podfile! Note that you will have to run a pod install command every time you switch branches for this to work.

Always put your Podfile.lock file under source control. Doing so ensures that the dependencies you pull into your development environment will be consistent with other team members or other machines. 

With the above tips, you should be able to really push CocoaPods to the limit without much effort. Feel free to leave a comment with any questions or comments you may have, and I will try my best to answer them for you!

  • Hok Shun Poon

    Have you found a better way to group pods up for multiple targets? I’ve got say, 3 targets that require an average of some of the same 30 dependencies.

    By using ‘def’, we repeat the dependencies several times, and I end up with around 90 targets. `pod install` takes an incredibly long time configuring these 90 targets to the point where it’s unbearable to iterate on the Podfile (you might want to do this to try different pods out).

    Have you found a better way?

  • Hey there! Unfortunately, I haven’t found a better way to do this. I am also in your boat: 6 build targets, 2 test targets, and around 100+ targets in the Pods xcodeproj project. It hasn’t been terrible though…in fact `pod install` takes about a minute.

    I saw that there are options to play around with `link_with` and setting `exclusive` but I have not tried it yet…
    `exclusive` = http://guides.cocoapods.org/syntax/podfile.html#target
    `link_with` = http://guides.cocoapods.org/syntax/podfile.html#link_with

  • Hok Shun Poon

    Heya. Thanks for the reply @kleinlieu:disqus. It’s quite sad that Cocoapods was designed to be so target-centric. It does certainly **work**, so my hat off to the team. A minute is quite long! I’m glad to hear you can accept those sort of wait times but for me it’s almost a deal breaker!

    I’ve yet to try the latest version 0.35-rc. Hopefully there’s been some performance improvements. Cheers.

  • qspi

    Important to mention to your solution (what is marvelous btw 🙂 ) that the ‘:’ is a syntax error at the end of the ‘def’ line, you should write simply ‘def myCocoaPods’ to define a pod group. I hope I can preserve a couple of minutes to the upcoming readers.

  • I updated the post to fix the typo. Thanks for that catch @qspi:disqus !

  • qspi

    Very welcome!