Creating a CocoaPod

I have been meaning to look at CocoaPods as a way of managing dependencies on third party libraries for some time. There is a good Introduction to CocoaPods over on NSHipster. You should read that together with Mattt Thompson’s article on Stewardship on Open Source communities. I will quote just one sentence:

It’s not enough to dump a pile of source code somewhere and declare it “open source”.

So partly because I want to be a better community member but also to gain some practical experience in creating a CocoaPod I decided to revisit my old iOS Keychain wrapper project. This post collects my notes on converting and releasing that project as a CocoaPod which I hope will be useful to others. If you are a CocoaPod guru and I missed a trick let me know.

Installation

CocoaPods is a Ruby gem which can be a cause for alarm if you are not familiar with Ruby. Luckily OS X ships with a version of Ruby pre-installed which will run CocoaPods fine. Installation is as simple as typing the command:

$ sudo gem install cocoapods

Note: I am using the Ruby Version Manager rvm to manage Ruby and gem dependencies which removes the need for the sudo:

$ gem install cocoapods

For further installation details check the CocoaPods Getting Started Guide.

Preparation

Before getting into creating a CocoaPod I will clean up the project a little and adopt some good practises.

Project Organisation

Life will be easier if the library files are all under a single directory structure. The latest versions of Xcode separate the Xcode files, project files and test files into separate directories. I have reorganised the project files into three main directories to create a similar setup:

  • PasswordManager - contains the class files for the library
  • PasswordManagerTests - contains the unit test files
  • ExampleApp - contains the example app code for the library

Tagging a Release

CocoaPods rely on semantic versioning which in simple terms means using a version number like this:

MAJOR.MINOR.PATCH
Increment MAJOR version for incompatible API changes.
Increment MINOR version for backwards-compatible changes.
Increment PATCH version for backwards-compatible bug fixes.

It is important that the version in the Podspec file (which I will explain shortly) also exists as a tag in the git repository. I have not been good at tagging the repository so will fix that by adding an initial tag for version 0.0.1 and pushing that to the repository:

$ git tag -a 0.0.1
$ git push —tags

Once I have everything working I will update the tag and the Podspec file to 1.0.0.

Adding a License

Before you can add a library to the CocoaPod repository it must have a valid license file. There are a lot of useful sites if you want help on choosing an open source library. My project already contains a BSD-style license file in the project root directory so I am good to go.

Documenting a Library

It seems obvious but if you want other people to use your library it needs to be well documented. This means you should have a README file in the project root and for an Objective-C library create appledoc style documentation.

My library already has a pretty good README which helped when adding the missing appledoc comments to the library header file. Once again NSHipster has a good guide to get you started on appledoc.

Tests

The final preparation step is to have a good test suite for the library. CocoaPods does not dictate which test framework you use. My library already has a set of OCUnit based tests that still work for iOS 6 so I will stick with them.

Creating the CocoaPod

Creating the Podspec File

The Podspec file describes the version of a library and includes basic metadata such as the name, version, description and the location of the source repository. The Podspec file lives in the root project directory. You can create it from scratch but the easiest way is to use the pod spec create command with the path to the repository:

$ pod spec create https://github.com/kharrison/UYLPasswordManager
Specification created at UYLPasswordManager.podspec

If you have not yet tagged the repository you will get a warning and the Podspec will use version 0.0.1. Either way you will get a well commented initial version of a spec pre-populated with some basic data from the repository. Don’t forget to remove the comments. The final version is below:

Pod::Spec.new do |s|
  s.name = "UYLPasswordManager"
  s.version = "1.0.0"
  s.summary = "Simple iOS Keychain Access"
  s.description = <<-DESC
    The UYLPasswordManager class provides a simple wrapper around Apple Keychain
    Services on iOS devices. The class is designed to make it quick and easy to
    create, read, update and delete keychain items. Keychain groups are also
    supported as is the ability to set the data migration and protection attributes
    of keychain items.
    DESC
  s.homepage = "https://github.com/kharrison/UYLPasswordManager"
  s.license = { :type => "BSD", :file => "LICENSE" }
  s.authors = { "Keith Harrison" => "keith@useyourloaf.com" }
  s.social_media_url = 'https://twitter.com/kharrison'
  s.platform = :ios, "6.0"
  s.source = { :git => "https://github.com/kharrison/UYLPasswordManager.git", :tag => "1.0.0" }
  s.source_files = "PasswordManager"
  s.requires_arc = true
end

Spec Metadata

The initial metadata section of the Podspec file is straightforward and mostly populated from the repository. I only needed to add the description text and change the homepage URL to point to the github project page.

s.name = "UYLPasswordManager"
s.version = "1.0.0"
s.summary = "Simple iOS Keychain Access"
s.description = <<-DESC
  The UYLPasswordManager class provides a simple wrapper around Apple Keychain
  Services on iOS devices. The class is designed to make it quick and easy to
  create, read, update and delete keychain items. Keychain groups are also
  supported as is the ability to set the data migration and protection attributes
  of keychain items.
  DESC
s.homepage = "https://github.com/kharrison/UYLPasswordManager"

Spec License

I am using a BSD license stored in a file named LICENSE in the project root so we can specify that in the Podspec file:

s.license = { :type => "BSD", :file => "LICENSE" }

Author Metadata

The author or maintainer of the library. If you prefer you can omit the email address.

s.authors = { "Keith Harrison" => "keith@useyourloaf.com" }

You can also add a social media URL. If you specify a Twitter account it will receive notifications from the @CocoaPodsFeed account.

s.social_media_url = 'https://twitter.com/kharrison'

Platform Specifics

The supported platform and deployment target. This is an iOS only library that supports iOS 6.0 and later:

s.platform = :ios, "6.0"

Source Location

The location of the source repository. Typically the Git repository and release tag:

s.source = { :git => "https://github.com/kharrison/UYLPasswordManager.git", :tag => 1.0.0 }

Source Code

With the library files cleanly contained in a single directory the source file location is easy:

s.source_files = "PasswordManager"

Build Settings

Not yet a default so we need to say if we expect ARC:

s.requires_arc = true

Checking the Podspec File

Before committing the Podspec file to the repository we can test from the project directory.

$ pod lib lint
 -> UYLPasswordManager (1.0.0)
UYLPasswordManager passed validation.

Assuming everything passes we can make the final commit of the changes to the repository, create the tag and push it to the repository. I first pushed a beta version (1.0.0-beta) to allow me to check the AppleDocs. When I was sure the CocoaPod was good I updated to 1.0.0:

$ git tag ‘1.0.0’
$ git push —tags

To test against the files in the repository:

$ pod spec lint UYLPasswordManager.podspec
 -> UYLPasswordManager (1.0.0)
Analyzed 1 podspec.
UYLPasswordManager.podspec passed validation.

Testing

Before releasing the CocoaPod to the world it is a good idea to test it in a real project.

Creating a Test Project

Create a new Xcode project to exercise the library and add the Podfile to the project root directory

platform :ios, '7.0'
pod 'UYLPasswordManager', :git => 'https://github.com/kharrison/UYLPasswordManager.git'

The git reference will retrieve the CocoaPod directly from the Git repository without referring to the central CocoaPods directory. To install the pod, from the project directory:

$ pod install

This will create an Xcode workspace which you can use to check the library installed without problems. Take a look at the Using CocoaPods guide for more details on adding pods to an Xcode project.

Publishing the CocoaPod

The CocoaPods Trunk Service is now the preferred way to publish a CocoaPod.

Register

Before you can publish a CocoaPod you need to register with the CocoaPods Trunk service. Make sure you use the email address that you use for committing and verify by clicking on the link in the email you receive.

$ pod trunk register keith@useyourloaf.com “Keith Harrison”

Publish to Trunk

The final step is to push the Podspec to the trunk service:

$ pod trunk push UYLPasswordManager.podspec

If you included a Twitter account in the Podspec you should see a notification within a few minutes announcing the new pod to the world.