Here at Square, we use CocoaPods as a core part of our iOS development workflow, with extensive libraries within the Square Point of Sale app alone, in addition to libraries used within our test suites. Many of these libraries live inside our main iOS monorepo, and as the app has grown, so has the time needed to build the whole thing.
CocoaPods has allowed us to keep all of our library configuration in easy-to-read podspec files, which are much easier to review in a pull request than Xcode projects. Very little of our project configuration is managed within Xcode itself, which ensures that we have consistency between the file system and what we see in Xcode.
There are some drawbacks, however, to integrating all of these modules into a single project. While we’ve invested effort in improving the overall performance of installing dependencies, running a single pod install command can take a minute or two, which adds up when quickly iterating on a single library. Using CocoaPods also generates an extremely large Pods project that can cause Xcode to become less responsive and take a long time to index.
With this sort of explosive growth, it became apparent that we needed a better way for developers to work. We want to enable working on modules in isolation, building only what’s actively being worked on, and running only the accompanying test suite.
Enter pod gen.
We built a CocoaPods plugin that allows iOS developers to easily work on individual libraries in isolation, particularly in the context of a monorepo. Running this plugin will generate a stub application project that integrates and links a specified library and its dependencies. Our full Pods project takes about 95 seconds to generate, and is 356 thousand lines. If we run pod gen on our UI components library, it takes 5 seconds and generates a 9 thousand line project file. This makes iterating on these fundamental libraries so much quicker, and tools handle the smaller scale with ease. The coolest thing about gen is that it automatically pulls in dependency restrictions from an existing Podfile and Podfile.lock, so all configuration is mirrored in the generated library workspace.
This isn’t only useful in the context of a monorepo for isolated development. It also works very well in standalone repositories, when used in conjunction with the ability to describe tests from within a podspec. This enables standalone libraries to avoid checking in and maintaining their own Xcode projects, since CocoaPods can manage it instead. This means you no longer have to deal with Xcode conflicts in your PRs since the .xcodeproj is simply a generated artifact used for development. The podspec itself becomes the source of truth for describing your library, from which you can generate a workspace with a simple pod gen command.
Tests can simply be placed on the file system in the appropriate folder and have them get picked up to run as part of CI. For example, we set up this demo repo and the only configuration we need to check in are the podspecs. We’ve been using this gem internally for several months, and we’re very excited to share it with the world!