CocoaPods
This page is about CocoaPods, a dependency manager for Swift and Objective-C Cocoa projects.
CocoaPods is a Ruby gem that is usually installed system-wide. The Ruby gem is then used to install and maintain a Git repository of packages, so-called "Pods", and to populate a software project with Pods from the repository.
References
- Website: https://cocoapods.org/
- First steps: https://guides.cocoapods.org/using/getting-started.html
- https://guides.cocoapods.org/using/using-cocoapods.html. This has an interesting section "Pods and Submodules" that explains the similarities of Git submodules and CocoaPods, and outlines an approach how to switch from Git submodules to CocoaPods.
Installation
System-wide Ruby gem
There are two options how the system-wide Ruby gem can be installed:
- Homebrew
- Use the system Ruby
In both cases you need to switch user to an account with admin privileges, then run one of the following commands in a Terminal session:
brew install cocoapods # Installation via Homebrew, no sudo needed sudo gem install cocoapods # Installation via system Ruby, sudo needed
As a result a new command line tool will be at your disposal:
pod [...]
User-specific package repository
Switch user to dev account, then run this command in a Terminal session to create the CocoaPods package repository.
pod setup
The command is documented here. The package repository is sometimes also called the "specs repo" or the "master repo".
The package repository is located here:
~/.cocoapods/repos
The repository is a Git repository that is a clone of https://github.com/CocoaPods/Specs.
At the time of writing, the repo size was 743 MB 4.3 GB, so just from the amount of data alone it can be expected for the initial cloning to take a while. In my case, however, I experienced much worse performance, because for some reason CocoaPods seems to download podspecs individually one by one - at least that's how it looks when it outputs pod names one after the other. On January 17 2022 the setup took roughly 16 hours to complete!
macOS 10.14 vs. Xcode 11
On macOS 10.14.6 with Xcode 11.2.1 installed I had problems installing CocoaPods. The issue is that Xcode 11 ships the macOS 10.15 SDK which includes headers for ruby2.6, but not for macOS 10.14’s ruby2.3.
To fix the problem I followed the instructions in this article (also check the underlying SO post), like so:
sudo xcode-select --switch /Library/Developer/CommandLineTools sudo gem install cocoapods sudo xcode-select --switch /Applications/Xcode-11.2.1.app
Uninstall
System-wide Ruby gem
The main Ruby gem to uninstall is named cocoapods
. Unfortunately, uninstalling that gem does not remove its unused dependencies, so these have to be removed separately. The following command gives you a list of Ruby gems with "cocoapods" in the name:
gem list --local | grep cocoapods
Another list of potential gems to uninstall is the one with the direct dependencies of the cocoapods
gem - make sure to ignore dependencies marked with "development". However, this list is much less safe to use because there might be other Ruby gems that you want to continue to use which have the same dependencies. Alas, Ruby apparently does not prevent you from uninstalling a gem even if another gem still needs it - at least I was able to uninstall, for instance, the dependency gem cocoapods-try
while the main Cocoapods gem was still installed. Anyway, the following command lists the direct dependencies of the main cocoapods
gem:
gem dependency cocoapods
Of course it does not end here: The above command only lists direct depenencies, but there may also be indirect dependencies (dependencies of dependencies). I currently do not know of any way how to find out these except by manually following the dependency chain. One noteable gem that I found was also placing a file in /usr/local/bin
was xcodeproj
- the presence of this gem conflicted, for instance, when attempting to install Cocoapods via Homebrew.
Once you have a list of all the gems you want to uninstall, use the command sudo gem uninstall <gem-name>
repeatedly. If one of the commands asks you whether to also remove an executable, answer "y" to that question. These were the gems I uninstalled when I attempted to get rid of Cocoapods before switching to Homebrew:
sudo gem uninstall cocoapods sudo gem uninstall cocoapods-deintegrate sudo gem uninstall cocoapods-downloader sudo gem uninstall cocoapods-plugins sudo gem uninstall cocoapods-search sudo gem uninstall cocoapods-trunk sudo gem uninstall cocoapods-try sudo gem uninstall xcodeproj
User-specific package repository
The package repository can be removed simply with rm
:
rm -rf ~/.cocoapods/repos
As far as I know there is nothing else in the ~/.cocoapods
folder, so it can also be removed
rm ~/.cocoapods
Updating CocoaPods and/or the package repository
To update the system-wide Ruby gem you simply install the gem again. A typical moment when this may be required is when you install a new version of Xcode and/or the command line tools.
The package respository is automatically updated when you issue certain CocoaPods commands (e.g. pod update
or pod outdated
, see below).
Begin using CocoaPods in a project
Run the following command while you are sitting in the project's root directory:
pod init Foo.xcodeproj
This creates the initial CocoaPods configuration file, which is named simply
Podfile
Edit the Podfile in a text editor and add some "pods". Here are some initial pointers. When you are finished, run this command:
pod install
This creates a new Xcode workspace (Foo.xcworkspace
) if one didn't exist yet. From now on you have to use the workspace instead of the Xcode project (Foo.xcodeproj
). In addition, the Xcode project is changed as follows:
- Adds
libPods.a
to the Xcode project's target's "Link With Libraries" build phase. If the Podfile specifies more than one target, I assume (but have not verified) that all of those targets' "Link With Libraries" build phase is modified. - Creates an
.xcconfig
file for each of the Xcode project's configurations and changes the project's configurations so that they are now based on those.xcconfig
files. Also adds the.xcconfig
files to the project, under a newly created folderPods
. The.xcconfig
files are actually stored in the filesystem folderproject_root/Pods/Target Support Files
. - Adds two new "Run Script" build phases to the Xcode project's target's build phases. The intended place for these is to be after all other build phases. The two new phases copy resources from any pods that the target is based on to the app bundle - one phase copies resources from frameworks, the other phase copies non-framework resources. I assume there is a technical reason why the two phases cannot be merged into one.
- Adds another new "Run Script" build phase to the Xcode project's target's build phases. This one checks whether the CocoaPods sandbox (i.e. the stuff in the
Pods
folder) is up-to-date and in sync with the specifications inPodfile.lock
Last but not least, this new file is generated after this first run of pod install
:
Podfile.lock
This file tracks the version of each Pod that was installed. When you come back to the project at a later time (e.g. after cloning the project repo on a new machine), CocoaPods will again install the same Pod versions that are noted in Podfile.lock
.
What to put under version control?
Podfile
Podfile.lock
- The workspace folder (excluding
xcuserdata
)
Note that I prefer to not add the Pods
folder to version control, although there is some debate whether it's better to do so.
pod install vs. pod update
The source of this section is this document.
- Use
pod install
to install a new pod or remove an existing pod, after having editedPodfile
. - Also use
pod install
to generate thePods
subfolder if it currently does not exist. The typical use case here is after cloning a Git repository, but you may also wish to re-generate the subfolder for other reasons. For instance, in the Little Go project I found it necessary to re-generate the subfolder after increasing the deployment target inPodfile
, because otherwisePods/Pods.xcodeproj
file was still using the old deployment target. - Use
pod update
to update existing Pods to a newer version. Note that this automatically updates the spec repo. If new versions are actually found and installed, thePodfile.lock
file is also updated to track the newly installed versions. - Use
pod outdated
to list Pods that have newer versions than the ones listed inPodfile.lock
. Note that this also automatically updates the spec repo.
Making a Pod
References
- The CocoaPods Guides site has several guides that help with making your own Pod, listed unter the common heading "Build with CocoaPods".
- One of the guides refers to an external tutorial on tutsplus.com.
Getting started
Make sure that the latest version of CocoaPods is installed. I wrote this section using CocoaPods 1.9.3.
Initialize the project with these commands:
cd ~/dev pod lib create SgfKit
This starts an interactive process that asks you some questions. Here are the answers I gave:
- Platform = iOS
- Language = ObjC
- Include a demo application = No
- Testing framework = Specta
- View based testign = No
- Class prefix = SGF
This is the result. As you can see a Git repository was created.
celebdil:SgfKit patrick$ ls -la total 40 drwxr-xr-x 11 patrick staff 352 14 Okt 18:54 . drwxr-xr-x 21 patrick staff 672 14 Okt 18:53 .. drwxr-xr-x 12 patrick staff 384 14 Okt 18:54 .git -rw-r--r-- 1 patrick staff 738 14 Okt 18:53 .gitignore -rw-r--r-- 1 patrick staff 543 14 Okt 18:54 .travis.yml drwxr-xr-x 9 patrick staff 288 14 Okt 18:54 Example -rw-r--r-- 1 patrick staff 1080 14 Okt 18:54 LICENSE -rw-r--r-- 1 patrick staff 895 14 Okt 18:54 README.md drwxr-xr-x 4 patrick staff 128 14 Okt 18:53 SgfKit -rw-r--r-- 1 patrick staff 1578 14 Okt 18:54 SgfKit.podspec lrwxr-xr-x 1 patrick staff 27 14 Okt 18:54 _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
Connect to GitHub
Create the empty project on GitHub. Configure the local Git repository Add a remote using the SSH URL shown on GitHub:
cd ~/dev/SgfKit git remote add origin git@github.com:herzbube/SgfKit.git
To be continued ...
I stopped exploring how to create a Pod because I realized that the library project I wanted this for has a wider scope than what is supported by CocoaPods. For instance I want the library to be cross-platform, at least macOS and iOS/iPadOS, but "pod lib create" forces me to choose between macOS and iOS.
So the story ends here, but it may be continued at a later time.