Creating a (private) Cocoa Pod

UPDATE 2017.06.22.

Verified that the methods described in the articel are still valid with XCode 8.3.2 and CocoaPods 1.2.0 (although the XCode porject cration window looks a bit different now).

UPDATE 2016.07.28.

Clarified some parts of Step 4 and 5 (when you need to add your Specs repo to your local CocoaPods repo and clarifying that naming the Specs repo can be different from the Pod's name).

Intro

When you are dealing with larger projects or multiple apps in the same project it is a good approach to separate reusable code into a library. On iOS, CocoaPods is a great tool to handle app dependencies, and it also enables to create your own private library as a Cocoa Pod. There are several tutorials for this (I linked the best of them at the end of this post), but when I first made a Cocoa Pod I found several pitfalls that these articles don't mention. This post is not a complete tutorial (you won't find an accompanying example project), but a list of steps that you have to take when creating your pod with additional tips for avoiding mistaskes (look for IMPORTANT NOTES at the end of each step. I'll also talk about how to use other CocoaPods dependencies in your own Pod. This guide assumes you are already familiar with using CocoaPods - if you are not, you can find a link to a tutorial from Raywenderlich.com at the end.

I used these when writing the article:
XCode 7.2.1, CocoaPods 0.39.0

But it is verified to be still valid with:
XCode 8.3.2, CocoaPods 1.2.0

Let's get to it

First of all, there is two main approach to create your Pod.
1. Create and implement your library as a standard Cocoa Touch Framework and then add the required podspec
2. Generate the project with CocoaPods' lib create command and then implement it

I will use the first approach as I found it more straight forward, especially when you use other Cocoa Pods in the Pod. I used Swift language but there is nothing language specific in this guide, so you can use Objective-C as well.

Step 1

alt Create a new XCode project as a Cocoa Touch Framework (found under iOS\Framework & Library). If you would like to test your library (and you should do it!), make sure that the Include Unit Tests checkbox is checked. CocoaPods enables you to use your library locally (I will show how later), but as soon as you are not the only one who works on the project, it is neccessary to upload it to a repository. You can use a private one (this guide will explore this option), but if you develop an open-source library you have to upload it to a public repository.

You will also need a separate repository for the Podspec (you can use one repository for all your private Pods), but if you develop an open source repository you can use the official CocoaPods repository (https://github.com/CocoaPods/Specs) for this.

If you plan to only use your library locally (or distribute it without uploading it to a repository), you should place your project in the ~/Documents/Libraries/ folder as this is a common place to put local libraries among iOS developers.

IMPORTANT NOTES: Your project must be inside the root folder of your repository, or else you won't be able to upload your spec and use your pod. It is possible to create your project first on your disk and then push the project folder to a repository, but always make sure it is in the root folder.

Step 1.5

This Step is only needed if you would like to use other Pods inside your own Pod.

Add CocoaPods to your project. Go inside your project folder in Terminal and issue the pod init command. It creates the Pod file that you need to declare your Pod dependencies. Open this file with XCode (you can use open -a Xcode Podfile in the terminal if you are still in the project folder). If you are familiar with using CocoaPods, you know how to declare dependencies, I will only show an example here (using Alamofire pod).

platform :ios, '8.0'  
use_frameworks!

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

target '[Your Project Name]' do  
    pod 'Alamofire', '~> 3.0'
end  

If you finished editing the Pod file, issue the pod install command in the terminal (make sure you are still in the project's root folder).

IMPORTANT NOTES: You shouldn't use TextEdit to edit your pod file as tends to replace standard quotes with typeset quotes. It is best to stick with XCode to edit Pod files.

These lines:

platform :ios, '8.0'  
use_frameworks!  

tell CocoaPods that your project is targeting iOS 8.0 and will be using frameworks instead of static libraries. It is best to enable it if you can target iOS 8.0 and above, and only disable it if you are so unfortunate that you have to support older iOS versions.

Step 2

Implement your library in XCode. Make sure you open the .xcworkspace from now on, as it is required when you work with CocoaPods. You can also write your unit test to make sure it is working well.

Step 3

Create your podspec file. This contains the neccessary details about your library that the consumers will need when including it in their projects. If you only use your library privately, you can ignore some of the details (for example the License file), but if you plan to release it to the public, make sure that every important details are present. You can read more about it here: https://guides.cocoapods.org/making/specs-and-specs-repo.html

You can create the podspec file manually (make sure it has .podspec as extension), but you can also use pod spec create [Your Pod Name] to generate a stub. You have to provide some basic details about your pod in the podspec file. It is written in Ruby, like the Pod file.

Here is an example:

Pod::Spec.new do |s|

  s.platform = :ios
  s.ios.deployment_target = '8.0'
  s.name = "[Your Pod Name, Same As The File Name]"
  s.summary = "[A Short Description of your Pod]"

  # Need to set this if you use ARC (Automatic Reference Counting)
  s.requires_arc = true

  # Replace the value below with your pod version
  s.version = "[Your Pod Version]"

  # Fill out the License details if you publish your library
  # or write something like "Custom" if it is only private
  s.license = { :type => "MIT", :file => "LICENSE" }

  # Replace with your name and e-mail address
  s.author = { "[Your Name]" => "[Your_Email@Your_Email_Domain.com]" }

  # You can replace this URL with your Repository page's URL
  #or the library's website if it has one (from the address bar, NOT the git URL)
  s.homepage = "[Your Pod Homepage URL]"

  # Replace the URL below with your own Repository's Git URL
  s.source = { :git => "[Your Pod Git URL]", :tag => "#{s.version}"}

  # List all the frameworks and pod dependencies that your pod uses
  s.framework = "UIKit"
  s.dependency 'Alamofire', '~> 3.0'

  # Specify where are your source files
  s.source_files = "[Your Pod Project Name]/**/*.{swift}"

  # You also have to specifiy where are your resources.
  # If you don't have any, comment this line.
  s.resources = "[Your Pod Project Name]/**/*.{png,jpeg,jpg,storyboard,xib}"
end  

IMPORTANT NOTES: You should be careful when creating this podspec file, because if it is not valid, you won't be able to upload it to the specs repo (even when you have a private one). You can run pod spec lint [Your Pod Name].podspec in the folder where the .podspec file is located to validate it (you should also use the --verbose option to read the full results). It will only validate after you uploaded the Pod to the repository as in Step 4.

If you are using other Pods in your own Pod, make sure you list them in the podspec file. The same goes for using frameworks like UIKit.

Make sure you increment your pod version each time you try to upload it. Also make sure that the podspec file's name matches the name specified as s.name or your podspec won't validate.

Step 4

Include your library in your app. I assume you have another project where you would like to use your newly created library. To do that, you have two options: include the file path in the Pod file or upload the Pod AND the Podspec to separate repositories, and specify that.

To inlcude a local pod (in the example I assume you put it in the ~/Documents/Libraries folder) you have to write this in the pod file (you can have other dependecies as well as shown in the below example):

platform :ios, '8.0'  
use_frameworks!

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

target '[Your Project Name]' do  
    pod '[Your Pod Name]', :path => '~/Documents/Libraries/[Your Pod Folder Name]'
    pod 'Alamofire', '~> 3.0'
end  

If you would like to upload the pod to a repository, you should start with the pod itself. Make sure you place the project in the root folder as I mentioned before. You can use git commands from the terminal, or any Git client.
Here is an example using terminal (make sure you are in the root folder of the project):

git init  
git add .  
git commit -m "Initial commit"  
git tag [Your Pod Version]  
git remote add origin [Your Pod Git URL]  
git push -u origin master --tags  

Then, you also have to upload the podspec to a separate repository. As I mentioned before, you can use one repository for all your private podspecs, and CocoaPod's official repository for public ones. You have to use these commands to upload (first add the Pod Specs repo to your local CocoaPods repo and then push the PodSpec of your repo to it):

pod repo add [Your Pod Specs Name] [Your Pod Specs Git URL]  
pod repo push [Your Pod Specs Name] [Your Pod Name].podspec  

You only need to add your Pod Spec repo to your local CocoaPods repo for the first time, you can push into an existing one at any time (at the next section we will do just that).

Finally, you can include your pod in the project where you want to use it. Just specify it in the pod file there (you can have other dependecies as well as shown in the below example):

platform :ios, '8.0'  
use_frameworks!

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

target '[Your Project Name]' do  
    pod '[Your Pod Name]', '~> [Your Pod Version]'
    pod 'Alamofire', '~> 3.0'
end  

You don't have to specify your Pod's version, but you should, as you will likely release never versions in the future.

IMPORTANT NOTES: For the pod repository, you have to have a tag on the master branch OR a separate branch with the same name that you specified in the podspec file. Do not have both, because you can get yourself to some serious troubles.

Always push the podspec from the command line with the pod command. This creates a local reference to your pod specs. You can find it in the ~/.cocoapods folder in your disk, make sure you delete it if you messed something up and want to try again.

Step 5

If you make changes to your library, you have to upload it with a different version. When you are ready to upload, change the version number in the podspec file (s.version). Than, upload the code to the Pod repository's master branch and add a tag with the new version number (or use a branch with the same name).
You can do these from the terminal like this:

git add .  
git commit -m "[Your Commit Message]"  
git tag [Your New Pod Version]  
git push -u origin master --tags  

Then you have to upload the updated podspec to the specs repository (you don't have to add the Specs repository to your local CocoaPods repositories again):

pod repo push [Your Pod Specs Name] [Your Pod Name].podspec  

When you are done with it, you have to specify the new version number in the Pod file of the project where you want to use your library:

platform :ios, '8.0'  
use_frameworks!

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

target '[Your Project Name]' do  
    pod '[Your Pod Name]' '~> [Your New Pod Version]'
    pod 'Alamofire', '~> 3.0'
end  

Finally, run the pod update command in the root folder of that project.

BONUS TIP: If you change the name of your Pod, but forget to change the import in your project where you use it, you may have to delete the derived data folder of Xcode, as the older Pod may still be accessible from your project, even when you update the Pod file and run pod install. You can find out where the folder is located (~/Library/Developer/Xcode/DerivedData) and how to delete it in this StackOverflow post: http://stackoverflow.com/questions/18933321/deleting-contents-from-xcode-derived-data-folder

Further reading:

Official guides:

https://guides.cocoapods.org/making/using-pod-lib-create
https://guides.cocoapods.org/making/making-a-cocoapod.html
https://guides.cocoapods.org/making/private-cocoapods.html
https://guides.cocoapods.org/making/specs-and-specs-repo.html
http://blog.cocoapods.org/CocoaPods-Trunk/

Tutorials:

http://www.raywenderlich.com/97014/use-cocoapods-with-swift
http://www.raywenderlich.com/99386/create-cocoapod-swift

http://code.tutsplus.com/tutorials/creating-your-first-cocoapod--cms-24332