CocoaPods Tutorial for Swift: Getting Started

Joshua Greene
CocoaPods Tutorial for Swift

Use this CocoaPods tutorial for swift to learn how to manage third-party library dependencies.

Update Note: This tutorial has been updated to Xcode 8.3 and Swift 3.1 by Joshua Greene. The original tutorial was also written by Joshua Greene.

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 wondering, “What exactly is CocoaPods?”

It’s probably best to let the CocoaPods website provide the answer:

CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It has over 30 thousand libraries and is used in over 1.9 million apps. CocoaPods can help you scale your projects elegantly.

Wow, that’s a lot of libraries used in a ton of apps! Scaling your project elegantly sounds great too. :]

But, what is a dependency manager? And why do you even need one?

A dependency manager makes it easy to add, remove, update and manage third-party dependencies used by your app.

For example, instead of reinventing your own networking library, you can easily pull in Alamofire using a dependency manager. You can even specify the exact version to use or a range of acceptable versions.

So even if Alamofire gets updated with backwards-incompatible changes, your app can continue using the older version until you’re ready to update it.

CocoaPods Tutorial

In this tutorial, you’ll learn how to use CocoaPods with Swift. Specifically, you’ll:

  • Install CocoaPods.
  • Work with a functional demo app that gets you thinking about ice cream.
  • Use CocoaPods to add networking.
  • Learn about semantic versioning.
  • Add another library using a flexible version.
  • Note: This CocoaPods 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 doing 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.

    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:

    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 set up to use CocoaPods!

    That was easy!

    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 CocoaPods tutorial starter project from here.

    Open IceCreamShop.xcodeproj, 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 functionality.

    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:

    • PickFlavorViewController is the view controller for this scene. It handles user interaction and 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 gets colors from a Flavor model.

    While every Ice Cream Shop location will have signature flavors in common, each carries their own local flavors too. For this reason, the data contained Flavor instances need 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’ll see a stubbed method:

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

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

    While you could use URLSession and write your own networking classes, there’s an easier way: use Alamofire!

    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 the following command:

    pod init
    

    This creates a Podfile for your project.

    Finally, type the following 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 replaces standard quotes with more graphically appealing typeset quotes. This can cause CocoaPods to become confused and throw errors, so it’s best to use Xcode or another programming text editor to edit your Podfile.

    The default Podfile looks like this:

    # Uncomment the next line to define a global platform for your project
    # platform :ios, '9.0'
    
    target 'IceCreamShop' do
      # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
      use_frameworks!
    
      # Pods for IceCreamShop
    
    end
    

    Delete the # and space before platform, and delete the other lines starting with #.

    Your Podfile should now look like this:

    platform :ios, '9.0'
    
    target 'IceCreamShop' do
      use_frameworks!
    
    end
    

    This tells CocoaPods your project is targeting iOS 9.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 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 CocoaPods.

    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 “fat” static libraries. “Fat” means they contained several code instruction sets (e.g. i386 for the simulator, armv7 for devices, etc.), but static libraries 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, right after use_frameworks!:

    pod 'Alamofire', '4.4.0'
    

    This tells CocoaPods you want to include Alamofire version 4.4.0 – 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, after ensuring 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 (4.4.0)
    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 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 open the project with the .xcworkspace file and not the .xcodeproj, 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.

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

    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:

    fileprivate func loadFlavors() {
    
      // 1
      Alamofire.request(
        "https://www.raywenderlich.com/downloads/Flavors.plist",
        method: .get,
        encoding: PropertyListEncoding(format: .xml, options: 0)).responsePropertyList {
          [weak self] response in
    
          // 2
          guard let strongSelf = self else { return }
    
          // 3
          guard response.result.isSuccess,
            let dictionaryArray = response.result.value as? [[String: String]] else {
              return
          }
    
          // 4
          strongSelf.flavors = strongSelf.flavorFactory.flavors(from: dictionaryArray)
    
          // 5
          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 you can set properties on it later.
    3. You next verify the response.result indicates it was successful, and the response.result.value is an array of dictionaries.
    4. If all goes well, you set strongSelf.flavors to an array of Flavor objects created by a FlavorFactory. This is a class a “colleague” wrote for you (you’re welcome!), which takes an array of dictionaries and uses them to create instances of Flavor.
    5. Lastly, you reload the collection view and select the first flavor.

    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 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 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 the 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', '~> 1.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 ~> 1.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 1.0.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 which aren’t backwards compatible, 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 ~> 1.0 means you should install the latest version that’s greater than or equal to 1.0 but less than 2.0.

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

    There’s 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.showAdded(to: contentView, animated: true)
      hud.label.text = "Loading..."
    }
    
    private func hideLoadingHUD() {
      MBProgressHUD.hide(for: contentView, animated: true)
    }
    

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

    fileprivate func loadFlavors() {
      showLoadingHUD() // <-- Add this line
    
      Alamofire.request(
        "https://www.raywenderlich.com/downloads/Flavors.plist",
        method: .get,
        encoding: PropertyListEncoding(format: .xml, options: 0)).responsePropertyList {
          [weak self] response in
    
          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 while the flavors are 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 lots 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 CocoaPods 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!

Team

Each tutorial at www.raywenderlich.com is created by a team of dedicated developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Joshua Greene

Joshua Greene is a passionate iOS developer who loves creating elegant apps. He's recently founded a boutique development shop called "Harmony Innovations." He's quickly learning there's much more to software development than just "software development."

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

Save time.
Learn more with our video courses.

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

... 20 total!

iOS Team

... 74 total!

Android Team

... 30 total!

Unity Team

... 12 total!

Articles Team

... 14 total!

Resident Authors Team

... 25 total!

Podcast Team

... 7 total!

Recruitment Team

... 9 total!