How to Create a CocoaPod in Swift

In this tutorial, you’ll learn how to create a CocoaPod containing your Swift code, assets, and storyboard files. You’ll also learn all about Podspec files.

Version

  • Swift 4.2, iOS 12, Xcode 10
Update note: Keegan Rush updated this tutorial for Xcode 10.0, Swift 4.2, and CocoaPods 1.5.3. Joshua Greene wrote the original.

You’re probably familiar with some well-known, open-source CocoaPods dependencies like Alamofire or MBProgressHUD. But sometimes you can’t find a pod with the exact functionality you need, or you may want to separate a large project into smaller, reusable components.

Fortunately, it’s easy to create your own CocoaPod! Through this tutorial, you’ll learn the following:

  • How to create a CocoaPod to host shared, reusable code.
  • How to use CocoaPods with either frameworks or static libraries.
  • How to publish your new CocoaPod to your own spec repository.

If you’ve already created a Cocoa Touch framework for your component, you’ve done most of the hard work. If you haven’t, don’t panic, as it’s really straightforward.

If you’ve only ever created classes as part of an iOS app, that’s OK, too. You can easily create a new pod by pulling out classes and functionality that make sense for your particular use case.

This tutorial picks up where How to Use CocoaPods With Swift ends. If you’ve never used CocoaPods before, then that tutorial is definitely a prerequisite to this one.

Otherwise, grab yourself a hot cup of “cocoa” (sorry, couldn’t resist!) and read on!

Getting Started

Note: This instructions and sample code in this tutorial require you to make Xcode 10 your default Xcode. To determine which Xcode is your default, enter xcode-select -p in a Terminal window. If your version is Xcode 9 or earlier, use the command sudo xcode-select -s path-to-Xcode-10 to change it.

Your top client is Ice Cream Shop, Inc. Its ice cream is so popular it can’t keep up with customer orders at the counter. It has recruited you to create a sleek iOS app that will allow customers to order ice cream right from their phones. You’ve started developing the app, and it’s coming along well.

Download the starter project using the Download Materials button at the top or bottom of this tutorial. This is an updated version of the final project from How to Use CocoaPods With Swift.

The app has a few pod dependencies already included in the download, so you don’t need to run pod install to install them.

Note: If you worked through How to Use CocoaPods With Swift, this next section may seem familiar; it’s simply a review of that tutorial. Feel free to skip ahead as needed.

Open IceCreamShop.xcworkspace and then Main.storyboard, found in the Views group, to see how the app is laid out.

Here’s a quick overview of the Choose Your Flavor scene, which is the heart of the app:

  • PickFlavorViewController: Handles user interaction, such as when the user selects an ice cream flavor.
  • PickFlavorDataSource: Is the data source for the collection view that displays ice cream flavors.
  • IceCreamView: Is a custom view that displays an ice cream cone and is backed by a Flavor model.
  • ScoopCell: Is a custom collection view cell that contains a ScoopView, which is also backed by an instance of the Flavor model class.

Storyboard Annotated

The top managers at Ice Cream Shop, Inc. really like how the app is coming along, but they’ve added a new requirement: Ice cream retailers need to have the same choose-your-own-flavor functionality in their apps.

Wait, that wasn’t in the original design. But that should be no problem for a Swift ninja like you!

Swift Ninja

Can you guess how you’re going to do this? Yep, you’re going to pull this functionality into its own CocoaPod!

Setting Up Your Pod

Create a new Xcode project and select iOS ▸ Framework & Library ▸ Cocoa Touch Framework, and then click Next:

New Cocoa Touch Framework

Enter RWPickFlavor for the product name and Swift for the language. Click Next.

For the purposes of this tutorial, you’ll create the project in ~/Documents/Libraries. Choose the Documents folder in your home directory. If you don’t already have a Libraries folder, click on the New Folder button in the bottom and create it.

Finally, choose the Libraries folder and click Create.

Note: The directory where you save your development pods is important because you’ll need to reference this directory from your Podfile during local development.

Normally, when you use CocoaPods, you include dependencies in your Podfile like this:

pod 'PodName', '~> 1.0'

But when you’re developing your own CocoaPod, you’ll instead specify a local path, like this:

pod 'MyPodName', :path => '~/Path/To/Folder/Containing/My/Pod'

There are two benefits to this approach:

  1. It uses the local files on your machine for the pod, instead of fetching them from a remote repository.
  2. When you’re using a pod, you normally shouldn’t make changes to any of the pod’s classes because these will be overwritten the next time you run pod install. By using the :path => syntax, your local path is treated as the source for the CocoaPod, and any changes you make will change the files in this location. So your changes will still exist even when you run pod install again.

While you can use a different location for your development pods, in general, put them in ~/Documents/Libraries. This is also a good location if you work with a team of developers since CocoaPods knows to expand the tilde as the user’s home directory.

You can also use other CocoaPods as dependencies of your pod. To do so, you need to create a Podfile for your CocoaPod to specify them during development.

Close Xcode, and enter the following commands in Terminal to create the Podfile:

cd ~/Documents/Libraries/RWPickFlavor
pod init
open -a Xcode Podfile

This creates a new Podfile and opens it in Xcode.

Replace the entire contents of the new Podfile with the following:

platform :ios, '12.0'

target 'RWPickFlavor' do
    pod 'Alamofire', '~> 4.7'
    pod 'MBProgressHUD', '~> 1.1.0', :modular_headers => true
end

This declares that RWPickFlavor will have external dependencies on both Alamofire and MBProgressHUD.

Using Swift Static Libraries

Prior to CocoaPods 1.5.0, CocoaPods did not work with static libraries. If you depended on a pod that contains Swift, you had to integrate CocoaPods into your project via frameworks instead of static libraries by specifying use_frameworks! in your Podfile.

This is no longer the case. As of CocoaPods 1.5.0, you can use CocoaPods with static libraries! This is a big deal, since not all libraries you might depend on are shipped as frameworks. Dynamic frameworks also come with a launch-time performance impact that some apps need to avoid.

By omitting use_frameworks! from your Podfile, CocoaPods will integrate into your app using static libraries instead. Your Objective-C dependencies still need to work as a module, however. MBProgressHUD is an Objective-C dependency that does not support modules. Luckily, by adding :modular_headers => true to the MBProgressHUD dependency in the Podfile, CocoaPods adds module support for us.

Note: You’ll need at least CocoaPods version 1.5.0 for this to work. If your version is less than 1.5.0, enter the following in Terminal to install the latest version of CocoaPods:
sudo gem install CocoaPods

Populating the New Pod

Save and close the Podfile, and then enter the following command in Terminal:

pod install

Just as you’d expect, this creates a workspace and installs the various requisite files.

Enter the following command in Terminal to open the newly created RWPickFlavor workspace:

open RWPickflavor.xcworkspace

Your Project navigator should now look like the following:

RWPickFlavor New Pod Files Hierarchy

You now need to copy a few of the existing files from the IceCreamShop workspace into RWPickFlavor. Open IceCreamShop in Finder. You’ll find the following sub-folders:

  • Categories
  • Controllers
  • Models
  • Views
  • Resources

IceCreamShop Folder Structure

Copy (don’t Move) them into the folder for RWPickFlavor, so that RWPickFlavor’s folder structure looks like this:

RWPickFlavor folder structure

Next, open RWPickFlavor.xcworkspace and add the folders you copied to the project using the Project navigator by selecting File ▸ Add Files to “RWPickFlavor”… and choosing the five folders you just added. Command-click each folder after the first to add it to the selection. When you’re done, your Project navigator should look like the following:

RWPickFlavor Files

Once you’re sure all the files have been copied over, delete the following groups from IceCreamShop.xcworkspace:

  • Categories
  • Controllers
  • Models
  • Views

Take care not to delete any of the following:

  • AppDelegate.swift
  • LaunchScreen.xib
  • Anything under the Supporting Files group
  • The Resources group

Next, back in the IceCreamShop workspace, open Info.plist, found under the Supporting Files group, and delete the line for Main storyboard file base name.

Are you wondering why you didn’t delete the Resources group? It contains the Images.xcassets asset catalog, which has the app icon and launch screen logo that you need. It doesn’t need the background image anymore, because that belongs in the RWPickFlavor pod. You can delete background from the IceCreamShop asset catalog.

Inside the RWPickFlavor workspace, delete the AppIcon and logo images from Images.xcassets. Now, your assets should be structured like this:

  • AppIcon and logo inside IceCreamShop
  • background inside RWPickFlavor

Also inside the RWPickFlavor xcworkspace, you need to make sure that the objects inside Main.storyboard are linked correctly. Find these three items inside the storyboard:

  • Choose Your Flavor
  • Ice Cream View
  • Pick Flavor Data Source

Check Linking

Because you copied this storyboard from a different project, you need to ensure that the module for each of these items is correctly set to your new CocoaPod, RWPickFlavor.

Check Linking to Module

Oftentimes, simply opening up the storyboard is enough to update it from its old module to the new one.

Note: Make sure to save your changes to the storyboard file with Command-S, or it’ll still be set to the old module.

Now, back to IceCreamShop.xcworkspace. Build and run. You shouldn’t see any errors, and you’ll eventually see the “Ice Cream Shop” logo followed by a black screen.

Believe it or not, the hardest part of creating your pod is done!

Dance Time!

Using CocoaPods and Git

Since CocoaPods is bootstrapped on top of Git, each pod will need to have its own Git repository. If you already have a preferred Git host, great — you can use it to host your repository.

If you don’t, GitHub is an excellent choice as it’s well-known by developers and has a free plan for open-source projects.

Bitbucket is another great option as it has a free unlimited tier, including private repositories, for teams of up to five developers.

This tutorial uses GitHub, but feel free to use your preferred Git host instead.

Setting Up Your GitHub Repo

First, Sign up or Login to your GitHub account.

Next, click on the + (create new) icon on the top right of the screen and select New repository as shown below:

Github: New Repository

Enter RWPickFlavor for the Repository name, and select Create repository.

GitHub will create a new repository under your account; you’ll then see the following screen with a Quick setup section that displays your repository URL:

GitHub Quick Setup

You’ll need this URL in just a moment, so leave the page open for now.

Now you need a second repository to host all of your private pod specs — you’ll use this later on in the tutorial.

Open github.com in a new tab; again, press the Create new icon and select New repository. Name this repository RWPodSpecs, and select Create repository.

Leave this tab open as well so you can easily grab the URL later when you need it.

Setting Up Podspec

Without a Podspec, RWPickFlavor is nothing more than a bunch of files. The Podspec is what defines an actual CocoaPod. The Podspec includes basic information such as the pod’s name, version and Git download URL.

You need to create the RWPickFlavor.podspec file for RWPickFlavor. Enter the following commands in Terminal, pressing Enter after each one:

cd ~/Documents/Libraries/RWPickFlavor
pod spec create RWPickFlavor
open -a Xcode RWPickFlavor.podspec

This creates RWPickFlavor.podspec and opens it in Xcode.

There’s a lot of excellent documentation and examples in the default Podspec. However, you don’t need most of it.

Replace everything in RWPickFlavor.podspec with the following:

Pod::Spec.new do |s|

# 1
s.platform = :ios
s.ios.deployment_target = '12.0'
s.name = "RWPickFlavor"
s.summary = "RWPickFlavor lets a user select an ice cream flavor."
s.requires_arc = true

# 2
s.version = "0.1.0"

# 3
s.license = { :type => "MIT", :file => "LICENSE" }

# 4 - Replace with your name and e-mail address
s.author = { "Keegan Rush" => "keeganrush@gmail.com" }

# 5 - Replace this URL with your own GitHub page's URL (from the address bar)
s.homepage = "https://github.com/TheCodedSelf/RWPickFlavor"

# 6 - Replace this URL with your own Git URL from "Quick Setup"
s.source = { :git => "https://github.com/TheCodedSelf/RWPickFlavor.git", 
             :tag => "#{s.version}" }

# 7
s.framework = "UIKit"
s.dependency 'Alamofire', '~> 4.7'
s.dependency 'MBProgressHUD', '~> 1.1.0'

# 8
s.source_files = "RWPickFlavor/**/*.{swift}"

# 9
s.resources = "RWPickFlavor/**/*.{png,jpeg,jpg,storyboard,xib,xcassets}"

# 10
s.swift_version = "4.2"

end

Just like a Podfile, the Podspec is written in Ruby. Be extra careful not to make any typos or else the pod will likely fail to validate or install later.

Here’s what’s going on:

  1. You first specify basic information about the pod.
  2. A Podspec is essentially a snapshot in time of your CocoaPod as denoted by a version number. When you update a pod, you’ll also need to update the Podspec’s version. All CocoaPods are highly encouraged to follow Semantic Versioning. If you’re not familiar with Semantic Versioning, see How to Use CocoaPods with Swift for more information.
  3. All pods must specify a license. If you don’t, CocoaPods will present a warning when you try to install the pod, and you won’t be able to upload it to CocoaPods trunk — the master specs repo.
  4. Here, you specify information about yourself, the pod author. Enter your name and email address instead of the placeholder text.
  5. Here, you need to specify the URL for your pod’s homepage. It’s OK to simply copy and paste the GitHub homepage from your browser’s address bar to use here.
  6. Replace this URL with the Git download URL from the “Quick Setup” section of the first repo you created above. In general, it’s best to use either a http: or https: URL to make it easier for other users to consume. You can use an SSH URL if you want, but you’ll need to make sure that everyone on your team — and whoever else needs access to the CocoaPod — already has their public/private key pairs set up with your Git host.
  7. Here, you specify the framework and any pod dependencies. CocoaPods will make sure that these dependencies are installed and usable by your app.
  8. Not all files in your repository will be installed when someone installs your pod. Here, you specify the public source files based on file extensions; in this case, you specify .swift as the extension.
  9. Here, you specify the resources based on their file extensions.
  10. Finally, specify 4.2 as the version of Swift used in the pod.

Some of the information you entered is important for CocoaPods to know how to download and install your pod correctly. Other information, such as the license, summary and author information, are there to help users to learn about your pod, if you choose to make it public in the CocoaPods Master Specs Repo.

Creating the License

Like every other pod, you’ll need to create the LICENSE file.

Copy and paste the MIT License found here into your favorite text editor, and then save the file as LICENSE — with no extension — in ~/Documents/Libraries/RWPickFlavor. Make sure to replace [year] and [fullname] with the actual values — the year and your actual name, of course!

Choose a License is a really great site that helps you choose the most appropriate open-source license for your project, and it’s built and maintained by the good folks at GitHub.

Pushing to Git

You’re finally ready to push the RWPickFlavor pod to its new home on GitHub!

Enter the following commands in Terminal, replacing [Your RWPickFlavor Git URL] with the Git URL for RWPickFlavor repo you created earlier:

cd ~/Documents/Libraries/RWPickFlavor
git init
git add .
git commit -m "Initial commit"
git tag 0.1.0
git remote add origin [Your RWPickFlavor Git URL]
git push -u origin master --tags

If prompted, enter your username and password for GitHub.

This commits all of the files within the RWPickFlavor directory, creates a new 0.1.0 tag and pushes everything to your remote repository.

Congratulations, you’ve just created your first CocoaPod!

Congratulations!

You’ve created your first CocoaPod, but can you actually use it? Well, not quite yet.

Using Your New CocoaPod

You first need to add your Podspec to a private specs repo; this lets CocoaPods find the pod when you try to install it. Fortunately, you’ve already created a Git repo for this, so this final step is relatively straightforward.

Enter the following in Terminal, making sure you’re still in the RWPickFlavor directory:

pod repo add RWPodSpecs [Your RWPodSpecs Git URL]
pod repo push RWPodSpecs RWPickFlavor.podspec

Make sure to replace [Your RWPodSpecs Git URL] with the Git URL for RWPodSpecs repo you created earlier.

This creates a local reference to RWPodSpecs that’s stored in ~/.cocoapods on your machine, and pushes the RWPickFlavor.podspec to it.

You now have a private pod specs repo set up! Easier than you thought, right?

With that complete, it’s finally time to use your newly created pod.

Open the Podfile for IceCreamShop and replace its contents with the following:

platform :ios, '12.0'

source 'https://github.com/CocoaPods/Specs.git'
source '[Your RWPodSpecs Git URL Goes Here]'

target 'IceCreamShop' do
  pod 'RWPickFlavor', '~> 0.1.0'
  pod 'MBProgressHUD', '~> 1.1.0', :modular_headers => true
end

Make sure that you replace [Your RWPodSpecs Git URL Goes Here] with the Git URL for your RWPodSpecs repo. You don’t need to include Alamofire in the Podfile, because it’ll be pulled in as a dependency defined in RWPickFlavor.podspec. You still need a line for MBProgressHUD so that you can include the :modular_headers => true configuration.

Close IceCreamShop.xcworkspace. Then, run pod install in Terminal.

Finally, reopen IceCreamShop.xcworkspace and replace the entire contents of AppDelegate.swift with the following:

import UIKit
import RWPickFlavor

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  
  var window: UIWindow?
  
  func application(_ application: UIApplication, didFinishLaunchingWithOptions
    launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    
    window = UIWindow(frame: UIScreen.main.bounds)
    window?.rootViewController = createRootViewController()
    window?.makeKeyAndVisible()
    
    return true
  }
  
  func createRootViewController() -> UIViewController {
    let bundle = Bundle(for: PickFlavorViewController.self)
    let storyboard = UIStoryboard(name: "Main", bundle: bundle)
    return storyboard.instantiateInitialViewController() ?? UIViewController()
  }
}

In createRootViewController, you get a reference to the RWPickFlavor “bundle” — which is actually a dynamic framework — to create a reference to the Main storyboard and instantiate its root view controller.

Build and run. You’ll be greeted with the familiar “Choose Your Flavor” screen. Awesome!

choose_flavor

Where to Go From Here?

If you want to download the finished project, use the Download Materials button at the top or bottom of this tutorial.

You’re now ready to start making your own CocoaPods! However, what you’ve covered in this tutorial is really just the tip of the iceberg when it comes to CocoaPods. Check out the CocoaPods Guides to learn everything you need to know about creating CocoaPods.

After creating a CocoaPod, you might consider adding it to the CocoaPods Master Specs Repo so that it will be made available via CocoaPods.org to thousands of developers around the world. Refer to the CocoaPods Trunk blog post to find out how you can do this!

If you have any questions or comments about the tutorial, feel free to join the discussion in the comments below!

Contributors

Comments