CocoaPods

From HerzbubeWiki
Jump to: navigation, search

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


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 folder Pods. The .xcconfig files are actually stored in the filesystem folder project_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 in Podfile.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 edited Podfile.
  • Also use pod install to generate the Pods 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 in Podfile, because otherwise Pods/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, the Podfile.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 in Podfile.lock. Note that this also automatically updates the spec repo.


Making a Pod

References


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.