Working With Private CocoaPods

Posted 3/6/2018.

In the last tutorial, we built a cross-platform Swift framework. If you want to reuse your frameworks in other projects, the next step is to support dependency managers. The three most popular right now are CocoaPods, Carthage, and Apple's Swift Package Manager. If your framework is open-source, these are all worth supporting. But what if you need to keep your framework's source code private? In this tutorial we'll explore how to do this with CocoaPods and GitHub.

Podspec & Podfile

To understand the framework setup to this point, you can download the example framework from the last tutorial here.

The first step in this process is to create a podspec file in the root directory of your project called "ExampleFramework.podspec" The podspec file is the CocoaPods standard for telling the dependency manager about our framework, its dependencies, supported platforms, and where to find the source code. Here is an example podspec for our framework:

Pod::Spec.new do |s|
    s.name = 'ExampleFramework'
    s.version = '1.0.0'
    s.summary = 'This is an example of a cross-platform Swift framework!'
    s.source = { :git => '[REPO URL]', :tag => s.version }
    s.authors = '[NAME / COMPANY NAME]'
    s.license = 'Copyright'
    s.homepage = '[WEBSITE URL]'

    s.ios.deployment_target = '9.0'
    s.osx.deployment_target = '10.9'
    s.tvos.deployment_target = '9.0'
    s.watchos.deployment_target = '2.0'

    s.source_files = 'Sources/**/*.swift'
    s.dependency 'Alamofire', '4.6.0'
end

OK let's break this down. The first section is pretty self-explanatory, just note the areas you need to change for your own framework and consider which license is appropriate for you. Also, note that in the "s.source" line we promised to tag a release with the version number we specified (1.0.0). This will be important later.

Next we specified the deployment targets for each platform. Here just make sure to use the minimum versions that make sense for your framework.

In the last section, we specify where to find the source files. Here we tell the podspec to look for any Swift files in the "Sources" folder and recursively in any of its subfolders. We also specify that the framework has one dependency, the popular networking library Alamofire. We don't have any code in the framework that uses Alamofire yet, but this will help demonstrate how to add dependencies to your framework.

Now we're ready to create the Podfile, in the same directory and simply called "Podfile", which will use the podspec we just created to pull in the Alamofire dependency. The Podfile will need to know where to get dependencies for each target. Here's how it should look:

use_frameworks!

target 'ExampleFrameworkMobile' do
    platform :ios, '9.0'
    podspec :path => 'ExampleFramework.podspec'
end

target 'ExampleFrameworkMac' do
    platform :osx, '10.9'
    podspec :path => 'ExampleFramework.podspec'
end

target 'ExampleFrameworkTV' do
    platform :tvos, '9.0'
    podspec :path => 'ExampleFramework.podspec'
end

target 'ExampleFrameworkWatch' do
    platform :watchos, '2.0'
    podspec :path => 'ExampleFramework.podspec'
end

In the directory with your Podfile, run pod install, close the Xcode project, and open the workspace that CocoaPods generated for you. You should now be able to build each framework target.

GitHub Setup

You're going to need to create two GitHub repos, both private. In our example the first would be for the framework itself, which is the root directory of our project. The second repo is for managing private pods, referred to as a "podspecs" repo. CocoaPods has a master podspecs repo where all of the public and open-source pods live. Since we want our framework to be private, we need to create a separate repo where CocoaPods will be able to find the podspec we created above. In our example we might call this "ExamplePodspecs".

Once you have both repos, you're ready to release the first version of the CocoaPod. First, we need to tag our release in Git so that the podspecs repo knows where to find the related version of our framework in the ExampleFramework repo. Make sure the version in the podspec and your project's Info.plist match exactly, then run the following with that version (e.g. "1.0.0"):

git tag 1.0.0
git push origin 1.0.0

Now that we have the tag, we can push the podspec file to the podspecs repo we created. You will need to replace "ExamplePodspecs" below with whatever you called your podspecs repo.

pod repo push ExamplePodspecs ExampleFramework.podspec

Pushing the podspec can take time, so you may want to add "--verbose" so you can see what's happening. Also, CocoaPods will validate your podspec and by default won't push it if there are warnings. It's important to resolve these warnings, but if you want to try publishing the podspec to your private repo anyway, you can add "--allow-warnings".

With that, we have released a private CocoaPod! To use it, we'll need to add the pod to the Podfile of an app target. This is like using public CocoaPods except for one addition. Since you will likely be pulling dependencies both from the CocoaPods master repo and your own, you will have to declare both podspec sources in your Podfile. An example iOS Podfile might look like this:

source '[PRIVATE PODSPECS REPO URL]'
source 'https://github.com/CocoaPods/Specs.git'

platform :iOS, '9.0'
use_frameworks!

target 'MyTarget' do

    # Internal
    pod 'ExampleFramework', '1.0.0'

    # Public
    # other publicly available pods you want to include

end

Using private CocoaPods is a great way to manage reusable Swift frameworks when you are building multiple proprietary apps and cannot publish source code. As we have seen, CocoaPods makes it easy to use a combination of public and private pods and declare support for all four of Apple's platforms.