Working

How To Manage Dependencies with CocoaPods

There are great open source projects out there maintained by dedicated individuals and companies. One big problem you may face when bringing these projects into yours is managing them.

When you bring in a 3rd party library you inherit a certain amount of risk. The biggest, of which, is that you can bring something in that could break your app. You download a new update, add files to your project, configure build settings, run your app, and then discover nothing works. You then panic, trace your steps backwards, and finally revert to the previous version… if you can remember which version you were using.

Dependency managers help solve some of this pain. You no longer need to worry so much about adding, removing, or updating 3rd party libraries manually.

By the time you finish this article you’ll learn about a widely used dependency manager called CocoaPods.

  • You’ll understand what CocoaPods does
  • You’ll learn how to integrate CocoaPods into your project
  • You’ll see a few tips that should help you on your journey
  • You’ll familiarize yourself with the process by walking through an example

Introducing CocoaPods

CocoaPods helps you focus on building apps instead of wrestling with 3rd party libraries. You avoid having to do so much manual work that can be error prone. This does mean you do give up some control in order for CocoaPods to work it’s magic. It’ll be up to you to decide if that’s good or bad.

The podspec

CocoaPods relies on a central library to manage what dependencies are available to install. You’ll often see a podspec file in a repository, like this one from the Alamofire repo.

Pod::Spec.new do |s|
  s.name = 'Alamofire'
  s.version = '1.1.4'
  s.license = 'MIT'
  s.summary = 'Elegant HTTP Networking in Swift'
  s.homepage = 'https://github.com/Alamofire/Alamofire'
  s.social_media_url = 'http://twitter.com/mattt'
  s.authors = { 'Mattt Thompson' => 'm@mattt.me' }
  s.source = { :git => 'https://github.com/Alamofire/Alamofire.git', :tag => s.version }
 
  s.ios.deployment_target = '8.0'
  s.osx.deployment_target = '10.9'
 
  s.source_files = 'Source/*.swift'
 
  s.requires_arc = true
end

One of the disadvantages of CocoaPods is that a repo needs to have this file available and be included in the central spec repo. At this point there are 6,836 specs so it’s very likely that the repo you want is there. However, at the end of the day you can actually create your own spec repo if you need to. I’ll cover that a little later.

In order for CocoaPods to know what dependencies you use for your project it reads a Podfile. The Podfile describes what repo to download from and how to configure your project.

Podfile Layout

A Podfile defines what your project depends on. Here we’ll review some of the more common things that make up a Podfile. These are highly configurable files, so reference the syntax over at CocoaPods for more.

platform

platform :iOS, '8.0'

In order for CocoaPods to set things up properly it needs to know what platform you’re targeting. This currently defaults to iOS 4.3, but you’ll want to default to 8.0 in order to work with Swift.

source

source 'https://github.com/CocoaPods/Specs.git'

The line above shows CocoaPods where to look for the pods you list. As I mentioned above, you could also include another source line that points to your own spec repo.

pod

pod 'Alamofire'

The previous line will be the most common line you’ll be working with in the Podfile. If you scan back to the Alamofire podspec you’ll see that we add the name from line #2. This will install the most recent version of Alamofire. Sometimes, that’s not what we want to have happen. We can specify the version we would like to use by explicitly pointing out what we need. For example, here we do not want pods beyond version 1.0 installed.

pod 'Alamofire', '1.0'

This makes sure that we don’t accidentally update our pods and install a future version that we didn’t want. Here are a few other ways to define the version number:

  • ‘> 1.0’ – Install versions greater than 1.0
  • ‘>= 1.0’ – Install 1.0 and future versions
  • ‘< 1.0' - Install versions less than 1.0
  • ‘<= 1.0' - Install any version before 1.0, and including 1.0

Most of the time I either leave the version off so I have the latest and greatest, or I explicitly note that I want a version.

Sometimes you need to use a specific version of a repository. To do that you’ll append where the repository is and what you’re looking for.

pod 'Alamofire', :git => 'https://github.com/Alamofire/Alamofire.git', :commit => '410e866'

This will grab a specific commit from the provided repository location. You can also use :branch or :tag instead of :commit.

pod :podspec

It’s important to point out that you may run into situations that warrant the need of using pods not available in the central repository. You could host your own repository on Github or just keep them local to your own network. This task is far beyond the scope of this article, but I want to make it clear that this is possible.

inhibit_all_warnings!

inhibit_all_warnings!

Since you have little control of the library you depend on it can help to silence warnings emitted. You can also do this on a per pod basis:

pod 'Alamofire', :inhibit_warnings => true

If you’re the kind of developer who likes a clean workspace this is helpful.

pre_install and post_install

Hooks give you the ability to make changes to Pods before and after they’ve been downloaded. You may need to do this if you need to configure additional build settings or other build-related tasks.

For example, maybe you want to use the Facebook pod Tweaks and you want to make sure the preprocessor macro FB_TWEAK_ENABLED is available and set to 1. You could use a post_install hook to make sure that’s configured properly.

post_install do |installer_representation|
    installer_representation.project.targets.each do |target|
        if target.name == "Pods-Tweaks"
            target.build_configurations.each do |config|
                if config.name == 'Release-Tomorrow-AdHoc'
                    config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FB_TWEAK_ENABLED=1']
                end
            end
        end
    end
end

A Quick Example

To give you a little taste of what it is like dealing with CocoaPods we’ll run through a quick example. We won’t dive into everything above, but you’ll be well equipped to begin using CocoaPods. At the end of the day I don’t stray too far away from just using the pod statement.

Install CocoaPods

CocoaPods is built with Ruby and will be installable with the default Ruby version available on your machine. To install CocoaPods open up the command line and type the following:

sudo gem install cocoapods

Swift Support

Support for CocoaPods + Swift is still under development, but they’ve recently released CocoaPods 0.36 beta. This introduced support for Swift and Frameworks. In order to update to this version you’ll want to append the —pre flag.

sudo gem install cocoapods —pre

At this point you’ll want to watch out for pods not being updated to handle being a framework. You’ll notice issue if you add a pod that requires being a framework because all of the other pods will become a framework. You may run into issues with files not being found if a pod isn’t ready to become a framework.

New Project

Now that CocoaPods is installed let’s create a new project.

New Project

Nothing fancy here, we’re just going to create a single view application.

Dojo Dependencies

Now that we have a project we need to add a few dependencies to it! In this example I’ll be adding a few of my favorites. First, we’ll need to create a Podfile to let CocoaPods know what we need. So right click on your project name in the file navigator and select New File. We want to create an empty file so navigate to iOS > Other > Empty. Save the Podfile at the root of your project.

Save Podfile

Now let’s edit the Podfile to include our desired pods, along with a few more details.

Edit Podfile

It’s time to install them! Close your project because from now on we’ll need to use a workspace that CocoaPods generates. Jump over to the Terminal and navigate to your project directory. Once you get there type pod install.

Pod Install

Don’t worry about the git repository error. Things will still work as expected.

Once CocoaPods completes the process it’s time to open the new workspace. You can type open dojo-dependencies.xcworkspace or navigate to the file via Finder.

Pod Install Verify

Reviewing our new workspace we can see that pods were added and that one of the pods we used required iOS 8.0 frameworks (Alamofire). We know this because we’re seeing *.framework under the Products group. Previously you would see static libraries (*.a) listed. You should be fine unless the owner of one of the pods you’re using failed to test their pod. It is a beta so your milage may vary (I’ve been fine).

You can now verify that things are in place by experimenting with the pods.

import UIKit

import pop
import Alamofire
import SlackTextViewController

// We'll subclass SLKTextviewcontroller
class ViewController: SLKTextViewController {
    
    var ball = UIView(frame: CGRectMake(0, 0, 100, 100))
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        
        ball.center = CGPointMake(view.bounds.size.width/2, 200)
        ball.backgroundColor = UIColor.blueColor()
        ball.layer.cornerRadius = ball.bounds.size.width/2
        ball.layer.masksToBounds = true
        view.addSubview(ball)
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        // POP
        let anim = POPSpringAnimation(propertyNamed: kPOPLayerScaleXY)
        anim.toValue = NSValue(CGPoint: CGPointMake(2.0, 2.0))
        anim.springBounciness = 20
        anim.springSpeed = 10
        ball.layer.pop_addAnimation(anim, forKey: "grow-ball")
        
        // Alamofire
        Alamofire.request(.GET, "http://httpbin.org/get").responseString { (_, _, string, _) in
            self.textInputbar.textView.text = string
        }
    }
}

CocoaPods and Source Control

A common question that I see relates to source control. CocoaPods generates a few things that you may or may not want to store in your repository.

  • Podfile – Include this or else people will have trouble installing the dependencies.
  • Podfile.lock – This file tracks the latest versions used on a project. If you grab the project and run pod install CocoaPods will inform you that a version has changed. I would check this in.
  • Workspace – No need to check this in. This’ll be generated when pod install is called.
  • Pods Directory – No need to check this in. Checking it in will help review changes to your 3rd party libraries, but is a bit overkill for everyday use cases.

A caveat to all of this is when you’re done with a project. Feel free to check everything in so you know you’ll have a copy of everything in the future.

Note For The Future You

Experienced developers likely know this already, but if you’re just getting started it pays to know that you don’t have to release full control to CocoaPods. Some favor having 100% control with how dependencies are managed and integrated. That’s fine. You can install pods with a —no-integrate flag that will keep CocoaPods from messing with your project.

pod install —no-integrate

Takeaway

If you run into any trouble visit the troubleshooting section at cocoapods.org.

As you can see using CocoaPods is pretty straightforward. It’s value really shines when you need to easily switch between library versions or point to a specific commit. You may find yourself doing this a lot since you can’t really push library owners to work the way you work. If they’re slow to pull a change of yours then you can just point the pod to your location. If you’re trying to stay bleeding edge, and a recent commit breaks something, then point the pod to a commit before the change.

Hope you enjoyed this article! Be sure to join the Discover iOS Letter.


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *