How to Use CocoaPods with Swift

Joshua Greene
CocoaPods Rocks!

Learn how to use CocoaPods to manage third-party dependencies.

Update 10/6/2015: Updated for Xcode 7.0 and Swift 2.0.

There’s been a lot of buzz about CocoaPods lately. You may have heard about it from another developer, or seen it referenced on a GitHub repository. If you’ve never used it before, you’re probably asking, “What exactly is CocoaPods?”

Well, it’s not a mystical pod of raw cacao hand-picked by Amazonian tribesmen, that’s for sure! It’s probably best to let the CocoaPods website provide the answer:

CocoaPods is the dependency manager for Cocoa projects. It has thousands of libraries and can help you scale your projects elegantly.

Scaling your project elegantly sounds intriguing, but what is a dependency manager? And why do you even need one?

No matter what kind of app you’re creating, you’re likely to use code from other developers in the form of frameworks and libraries. You’re likely familiar with UIKit and Foundation, both of which are examples of Apple-provided frameworks.

In this tutorial, you’ll:

  • Learn why third-party libraries are your friend.
  • Install CocoaPods.
  • Work with a functional starter project that gets you thinking about ice cream.
  • Install, use and modify a dependency to improve user experience.
  • Learn about semantic versioning.

Why Libraries are Your Friend

While you aren’t strictly required to use third-party libraries or frameworks, they can definitely save you a lot of time and let you focus on polishing your app instead of typing out countless lines of code that you simply don’t need to write.

You can use third-party frameworks and libraries without a dependency manager too, and you can get hands-on tutorials about them right here on this site. For example, there’s our Alamofire tutorial, and our SwiftyJSON tutorial.

Without a dependency manager, you simply add each library’s code to your project manually. However, this approach has several disadvantages:

  • Updating a library to a new version can be difficult, especially if several libraries must be updated together because one depends on another.
  • Including a library in your project makes it tempting to make local changes to the code, making it harder to update to a newer version later.
  • Determining the current versions of libraries used in your app can be hard to do, especially if you don’t proactively keep track of them.
  • Finding new libraries can be difficult without a central location to see all the available libraries.

CocoaPods helps you overcome all of these issues and more. It fetches library code, resolves dependencies between libraries, helps you search for and discover new libraries, and even sets up the right environment to build your project with minimum hassle.

Prerequisites

This tutorial requires basic familiarity with iOS and Swift development. If you’re completely new to iOS and/or Swift, then please check out some of the other written and/or video tutorials on this site before returning to this tutorial. Or, dive into the iOS Apprentice.

This tutorial also includes classes that use Core Graphics. While knowledge of Core Graphics is beneficial, it’s not strictly required. If you’d like to learn more about Core Graphics, read our Modern Core Graphics With Swift series.

This tutorial requires Xcode 7.0 and Swift 2.0.

Getting Started

You first need to install CocoaPods. Fortunately, CocoaPods is built on Ruby, which ships with all recent versions of Mac OS X. This has been the case since OS X 10.7.

Open Terminal and enter the following command:

sudo gem install cocoapods

Enter your password when requested. The Terminal output should look something like this:

Install CocoaPods

You must use sudo to install CocoaPods, but you won’t need to use sudo after it’s installed.

Lastly, enter this command in Terminal to complete the setup:

pod setup --verbose

This process will likely take a few minutes as it clones the CocoaPods Master Specs repository into ~/.cocoapods/ on your computer.

The verbose option logs progress as the process runs, allowing you to watch the process instead of seeing a seemingly “frozen” screen.

Awesome, you’re now setup to use CocoaPods!

That was easy!

Time to get coding!

Ice Cream Shop, Inc.

Your top client is Ice Cream Shop, Inc. Their ice cream is so popular they can’t keep up with customer orders at the counter. They’ve recruited you to create a sleek iOS app that will allow customers to order ice cream right from their iPhones.

You’ve started developing the app, and it’s coming along well. Download the starter project from here.

Open IceCreamShop.xcodeproj, and then build and run to see a mouth-watering vanilla ice cream cone:

IceCreamShop Starter

The user should be able to choose an ice cream flavor from this screen, but that’s not possible because you haven’t finished implementing this function.

Open Main.storyboard from the Views/Storyboards & Nibs group to see the app’s layout. Here’s a quick overview of the heart of the app, the “Choose Your Flavor” scene:

Storyboard Annotated

  • PickFlavorViewController handles user interaction, e.g. when the user selects an ice cream flavor.
  • PickFlavorDataSource is the data source for the collection view that displays the different ice cream flavors.
  • IceCreamView is a custom view that displays an ice cream cone, and it’s backed by a Flavor model.
  • ScoopCell is a custom collection view cell that contains a ScoopView, which is also backed by a Flavor model.

While each Ice Cream Shop location will have some signature flavors in common, each carries their own local flavors too. For this reason, the data contained in the instances of Flavor needs to be provided by a web service.

However, this still doesn’t answer the question, “Why can’t the user select an ice cream flavor?”

Open PickFlavorViewController.swift, found under the Controllers group, and you will see a stubbed method:

private func loadFlavors() {
  // TO-DO: Implement this
}

Ah-ha, there are no flavors! You need to // Implement this.

While you could use NSURLConnection or NSURLSession and write your own networking classes, there’s an easier way: Alamofire, an open-source networking library.

You might be tempted to simply download this library and drag the source files right into your project. However, that’d be doing it the hard way. CocoaPods provides a much more elegant and nimble solution.

So, without further ado…

Let's Do This!

Installing Your First Dependency

You first need to close Xcode.

Yeah, you read that right. It’s time to create the Podfile, where you’ll define your project’s dependencies.

Open Terminal and navigate to the directory that contains your IceCreamShop project by using the cd command:

cd ~/Path/To/Folder/Containing/IceCreamShop

Next, enter this command:

pod init

This creates a Podfile for your project.

Type this command to open the Podfile using Xcode for editing:

open -a Xcode Podfile

Note: You shouldn’t use TextEdit to edit the Podfile because it likes to replace standard quotes with more graphically appealing typeset quotes. This can cause CocoaPods to become confused and results in errors being thrown, so it’s best to use Xcode or another programming text editor to edit your Podfile.

The default Podfile looks like this:

# Uncomment this line to define a global platform for your project
# platform :ios, '6.0'
 
target 'IceCreamShop' do
 
end
 
target 'IceCreamShopTests' do
 
end

Replace the two commented lines with the following:

platform :ios, "8.0"
use_frameworks!

This tells CocoaPods that your project is targeting iOS 8.0 and will be using frameworks instead of static libraries.

In order to use CocoaPods written in Swift, you must explicitly include use_frameworks! to opt into using frameworks. If you forget to include this, and CocoaPods detects you’re trying to use a Swift CocoaPod, you’ll get an error when you try to install the pods.

If you’ve only ever programmed in Swift, this may look a bit strange − that’s because the Podfile is actually written in Ruby. You don’t need to know Ruby to use CocoaPods, but you should be aware that even minor text errors will typically cause CocoaPods to throw an error.

A Word About Libraries

You’ll see the term “library” often used as a general term that actually means a library or framework. This tutorial is guilty of casually intermixing these words too. In actuality, when someone refers to a “Swift library,” they actually mean a “Swift dynamic framework” because Swift static libraries aren’t allowed.

You may be wondering, “What’s the difference between a library, a framework and a CocoaPod?” And trust me, it’s okay if you find the whole thing a touch confusing!

A CocoaPod, or “pod” for short, is a general term for either a library or framework that’s added to your project by using the CocoaPods tool.

iOS 8 introduced dynamic frameworks, and these allow code, images and other assets to be bundled together. Prior to iOS 8, CocoaPods were created as static libraries, which are just “fat” binaries. This means they contain several code instruction sets (e.g. i386 for the simulator, armv7 for devices, etc.), but they aren’t allowed to contain any resources such as images or assets.

Another important difference is dynamic frameworks have namespace classes and static libraries don’t. So, if you had two classes called MyTestClass in different static libraries within a single project, Xcode would not be able to build the project because it would fail to link correctly citing duplicate symbols. However, Xcode is perfectly happy building a project that has two classes with the same name in different frameworks.

Why does this matter? Unlike Objective-C, the standard Swift runtime libraries aren’t included in iOS! This means your framework must include the necessary Swift runtime libraries. As a consequence, pods written in Swift must be created as dynamic frameworks. If Apple allowed Swift static libraries, it would cause duplicate symbols across different libraries that use the same standard runtime dependencies.

Fortunately, CocoaPods takes care of all of this for you. It even takes care of only including required dependencies once. All you have to do is remember to include use_frameworks! in your Podfile when working with Swift CocoaPods and you’ll be just fine.

Amazing, right?

Back to Installing Your First Dependency

It’s finally time to add your first dependency using CocoaPods. Add the following to your Podfile, inside the first target block:

pod 'Alamofire', '2.0.2'

This tells CocoaPods that you want to include Alamofire version 2.0.2 – the latest, stable version at the time of writing this tutorial – as a dependency for your project.

Save and close the Podfile.

You now need to tell CocoaPods to install the dependencies for your project. Enter the following command in Terminal, right after making sure that you’re still in the directory containing the IceCreamShop project and Podfile:

pod install

You should see output similar to the following:

Analyzing dependencies
Downloading dependencies
Installing Alamofire (1.1.4)
Generating Pods project
Integrating client project
 
[!] Please close any current Xcode sessions and use `IceCreamShop.xcworkspace` for this project from now on.

Open the project folder using Finder, and you’ll see that CocoaPods created a new IceCreamShop.xcworkspace file and a Pods folder in which to store all the project’s dependencies.

Note: From now on, as the command-line warning mentioned, you must always use the workspace and not the project, otherwise you’ll encounter build errors.

Excellent! You’ve just added your first dependency using CocoaPods!

Got the Win!

Using Installed Pods

If the Xcode project is open, close it now and open IceCreamShop.xcworkspace instead.

Open PickFlavorViewController.swift and add the following just below the existing imports:

import Alamofire

Hit ⌘+b to build the project. If all went well, you shouldn’t receive any compilation errors.

Next, replace loadFlavors() with the following:

private func loadFlavors() {
 
  // 1
  Alamofire.request(
    .GET, "http://www.raywenderlich.com/downloads/Flavors.plist",
    parameters: nil,
    encoding: .PropertyList(.XMLFormat_v1_0, 0), headers: nil)
    .responsePropertyList { [weak self] (_, _, result) -> Void in
 
      // 2
      guard let strongSelf = self else {
        return
      }
 
      var flavorsArray: [[String : String]]! = nil
 
      // 3
      switch result {
 
      case .Success(let array):
        if let array = array as? [[String : String]] {
          flavorsArray = array
        }
 
      case .Failure(_, _):
        print("Couldn't download flavors!")
        return
      }
 
      // 4
      strongSelf.flavors = strongSelf.flavorFactory.flavorsFromDictionaryArray(flavorsArray)
      strongSelf.collectionView.reloadData()
      strongSelf.selectFirstFlavor()
  };
}

Here’s the play-by-play of what’s happening:

  1. You use Alamofire to create a GET request and download a plist containing ice cream flavors.
  2. In order to break a strong reference cycle, you use a weak reference to self in the response completion block. Once the block executes, you immediately get a strong reference to self so that you can set properties on it later. You also create a flavorsArray variable to hold the plist array on success.
  3. You next switch on result to determine whether the response was successful or not and get the values out of it.
  4. If all goes well, you set self.flavors to an array of Flavor objects created by a FlavorFactory. This is a class that a colleague wrote for you, which takes an array of dictionaries and uses them to create instances of Flavor. Feel free to peruse the factory class if you’d like, but it’s not important for the rest of the tutorial.

Build and run, and… you’re greeted with an error message! What’s this about?

App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
Couldn't download flavors!

Starting in iOS 9, Apple has introduced App Transport Security. Unfortunately, the flavors plist is at an HTTP location (not HTTPS), so it’s prevented for download by default.

For now, you can get around this by adding an exception to the application’s plist. Open Supporting Files -> Info.plist and add the following to create an exception for RayWenderlich.com:

RayWenderlich.com ATS Exception

Here’s the XML for the above, so you can copy and paste these values:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>raywenderlich.com</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>

Build and run. You should now be able to choose an ice cream flavor!

Choose Flavor

Now For a Tasty Topping

The app is looking good, but you can still improve it.

Did you notice that the app takes a second to download the flavors file? You may not have if you’re on a fast Internet connection, but customers won’t always be so lucky.

To help customers understand that the app is actually loading something, and not just twiddling its libraries, you can show a loading indicator. MBProgressHUD is a really nice indicator that will work well here. And it supports CocoaPods, what a coincidence! :]

You need to add this to your Podfile. Rather than opening PodFile from the command line, you can now find it in the Pods target in the workspace.

Pods in Workspace

Open Podfile and add the following right after the Alamofire line:

pod 'MBProgressHUD', '~> 0.9.0'

Save the file, and install the dependencies via pod install in Terminal, just as you did before.

Notice anything different this time? Yep, you’ve specified the version number as ~> 0.9.0. What’s going on here?

CocoaPods recommends that all pods use Semantic Versioning.

Semantic Versioning

The three numbers are defined as major, minor, and patch version numbers. For example, the version number 0.9.0 would be interpreted as:

Semantic Versioning Example

When the major number is increased, this means that non-backwards compatible changes were introduced. When you upgrade a pod to the next major version, you may need to fix build errors, or the pod may behave differently than before.

When the minor number is increased, this means new functionality was added, but it’s backwards compatible. When you decide to upgrade, you may or may not need the new functionality, but it shouldn’t cause any build errors or change existing behavior.

When the patch number is increased, this means bug fixes were added, but no new functionality was added or behavior changes made. In general, you always want to upgrade patch versions as soon as possible to have the latest, stable version of the pod.

Lastly, the highest order number (major then minor then patch) must be increased per the above rules and any lower order numbers must be reset to zero.

Need an Example?

Consider a pod that has a current version number of 1.2.3.

If changes are made that are not backwards compatible, don’t have new functionality, but fix existing bugs, the next version would be 2.0.0.

Challenge Time

If a pod has a current version of 2.4.6 and changes are made that fix bugs and add backwards-compatible functionality, what should the new version number be?

Solution Inside SelectShow

If a pod has a current version of 3.5.8 and changes are made to existing functionality, what should the new version number be?

Solution Inside SelectShow

If a pod has a current version of 10.20.30 and only bugs are fixed, what should the new version number be?

Solution Inside SelectShow

Having said all this, there is one exception to these rules:

If a pod’s version number is less than 1.0.0, it’s considered to be a beta version, and minor number increases may include backwards incompatible changes.

So in the case of MBProgressHUB using ~> 0.9.0 means you should install the latest version that’s greater than or equal to 0.9.0 but less than 0.10.0.

This ensures you get the latest bug fixes when you install this pod, but won’t accidentally pull in backwards-incompatible changes.

There are several other operators available, too. For a complete list, see the Podfile Syntax Reference.

Showing Progess

Now, back in PickFlavorViewController.swift, add the following right after the other imports:

import MBProgressHUD

Next, add the following helper methods after loadFlavors():

private func showLoadingHUD() {
  let hud = MBProgressHUD.showHUDAddedTo(contentView, animated: true)
  hud.labelText = "Loading..."
}
 
private func hideLoadingHUD() {
  MBProgressHUD.hideAllHUDsForView(contentView, animated: true)
}

Now in loadFlavors(), add the following two lines:

private func loadFlavors() {
 
  showLoadingHUD()  // <-- Add this line
 
  // 1
  Alamofire.request(
    .GET, "http://www.raywenderlich.com/downloads/Flavors.plist",
    parameters: nil,
    encoding: .PropertyList(.XMLFormat_v1_0, 0), headers: nil)
    .responsePropertyList { [weak self] (_, _, result) -> Void in
 
      // 2
      guard let strongSelf = self else {
        return
      }
 
      strongSelf.hideLoadingHUD()  // <-- Add this line
      // ...

As the method names imply, showLoadingHUD() shows an instance of MBProgressHUD while the GET request downloads, and hideLoadingHUD() hides the HUD when the request is finished. Since showLoadingHUD() is outside the closure it does not need to be prefixed with self.

Build and run. You should now see a loading indicator:

IceCreamShop Loading

Great work! Customers can now select their favorite ice cream flavor, and they are shown a loading indicator while flavors are being downloaded.

Where to Go From Here

You can download the completed project from here.

Congratulations, you now know the basics of using CocoaPods, including creating and modifying dependencies, and understanding semantic versioning. You’re now ready to start using them both in your own projects!

There’s also lot more that you can do with CocoaPods. You can search for existing pods on the official CocoaPods website. Also refer to the CocoaPods Guides to learn the finer details of using this excellent tool. But be warned, once you do begin using it you’ll wonder how you ever managed without it! :]

I hope you enjoyed reading this tutorial as much I did writing it.

What are some of your favorite CocoaPods? Which ones do you rely on the most for everyday projects? Feel free to share in the comments below, or on the forums!

Joshua Greene

Joshua Greene is a passionate iOS developer who loves creating elegant apps. When he's not slinging code, he enjoys martial arts, Netflix, and spending time with his wonderful wife and daughter. You can reach him by email or on Twitter.

Other Items of Interest

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 9 total!

Swift Team

... 15 total!

iOS Team

... 34 total!

Android Team

... 16 total!

macOS Team

... 13 total!

Apple Game Frameworks Team

... 13 total!

Unity Team

... 10 total!

Articles Team

... 10 total!

Resident Authors Team

... 6 total!