Pods, Carthage, and SPM: Swift’s Package Management Dilemma

By 2019, Swift has evolved from one of Apple’s few open source projects to a full-fledged language serving as the powerhouse behind development for Apple systems, and had even spread to other operating systems such as Linux. Now in version 4.2, on the horizon the much-awaited release of Swift 5, Swift has become very popular for its protocol orientation and basis in the ability to write native apps for multiple platforms.

Swift 3 was a landmark release for all Swift’s users and developers: it was code-breaking release, changing most of its syntax, removing aspects of the language and adding others, and introducing other major tools and enhancements such as the Swift Package Manager and Swift Core Libraries for Linux. With Swift 3 came, however, another tough decision for developers. The Swift Package Manager, the third package manager for Swift developers but the first official one, was now in play, diversifying the selection and distribution process for libraries and frameworks even more.

Swift’s Package Management Dilemma

Google’s dictionary defines a dilemma as “a situation in which a difficult choice has to be made between two or more alternatives, especially equally undesirable ones.” Now, I wouldn’t go as far as to say all the available package managers are undesirable, but I could agree that none of them are perfect. As any piece of software does, the package managers have their triumphs and their shortcomings, though the shortcomings of each can lead to a hassle when they are used in development. The three main package managers for Swift are Cocoapods, Carthage, and the Swift Package Manager (SPM).

Cocoapods is the eldest of the group, having been around since the days when Objective-C was Apple’s main software development language. First released on September 17th, 2011, Cocoapods originally catered libraries and frameworks to those using the object-oriented C-esque language, paving the road for package managers that followed it. When Swift became mainstream, Cocoapods graciously expanded to encompass libraries and frameworks for the new language, the language in which the other package managers would later be built on. When November of 2014 rolled around, the next package manager was released, meaning that Cocoapods was no longer the sole distributor for those using Xcode. The ability to have multiple choices, all of which were strong, caused the original Swift package manager to come under a fire of controversy; while many back it as being revolutionary for Swift and Objective-C developers, others bombard it for its apparent faults.

One of Cocoapod’s most notable features was its complete automation of the dependency process, where developers only had to run pod install and Cocoapods would fetch and build the dependencies, insert them into the project, link them to it, and generate a workspace to encompass both the project and the pods. As it turns out, while the feature was a major convenience, developers were not entirely content with it: though the process did save them significant time and effort, it also took control of their project away from them, putting it into the hands of Cocoapods and letting its processes manipulate files and project settings. It was a silent pain the developers felt, one that was not voiced until the release of the next package manager. As time has gone on developers have been less content with allowing the popular third-party to take the reigns on building packages for their projects.

While newer package managers are written in Swift, for Swift, Cocoapods is written in Ruby, meaning developers do need a familiarity in the language to operate the package manager. Many people do know Ruby and the learning curve is not steep, so the inconvenience of adding files of another language is not too much a hassle; however, the fact that Swift’s package manager was not written in its own language, such as Nuget, PyPi, or NPM, and that another language had to be used is a pain for developers.

Cocoapods is centralized, something that is common in package managers (a common indicator is the package registry). Centralization in a package manager means that there is a central code registry where the packages are hosted and can be viewed, and where the client, usually a command line interface, can pull code from to generate the package. All of the Pods hosted on their network can be browsed from cocoapods.org, something that is helpful for developers who do not have a specific package in mind or just want to browse what is available. However, many seem to be straying towards the newer concept of decentralization, meaning Cocoapods is becoming less of the ideal package management system.

Though Cocoapods has its faults and has been the subject of scorn lately, it is still the oldest of Swift’s package managers, and is incredibly beginner-friendly due to its automation.

Carthage is the second package manager to come to the group, and its major differences from Cocoapods are apparent from the start. Released on November 18th, 2014, it was available just two months after the official release of Swift 1.1, and supported both Objective-C in Swift. Carthage picked up what was a sour topic for developers in Cocoapods: while Cocoapods was written in Ruby, Carthage caters to its users with a package manager written entirely in Swift. Along was the power of dependency management in the language Apple backs and that your project is written in, Carthage boasts that it leverages Xcode’s build system while giving the developer freedom in integrating their dependencies. With Carthage, the developer is given control over the dependencies of their project, allowing them to, after Carthage builds them, link and manage them as they choose.

Carthage’s README is incredibly descriptive and helpful when learning how to integrate Carthage into your project for the first time (and subsequently, for more complex needs), but to a developer with less experience it could be quite complicated to begin to use. With a longer integration process, however, comes more simplicity and control in the long run. Once Carthage is set up and the dependencies are linked, it only takes adding one line to an incredibly simple file (rather than a line or more to a Ruby file) and a single command, carthage update, to generate your new frameworks, where new ones can be imported the same way the original ones were. Carthage eliminates the hassle of dealing with multiple .xcproject files within a larger .xcworkspace, and removes the bulky file structure added along with your core project, instead only supplementing your project’s forward with .framework folders at your project’s root that clearly state the name of the framework.

Carthage offers a degree of control not seen in other package managers, of Swift, Objective-C, or other languages, and that is complete control. While it seems challenging at first to create the run scripts and link the frameworks to the project, the process becomes incredibly smooth once gotten used to. With the manual adding to the project and linking, the developer is given control over exactly what happens to their dependencies once Carthage builds them. If the developer decides they do not want a framework in the project, they do not have to edit the Cartfile and update all the dependencies; they can just remove the framework from the Xcode project and remove the linking command from the run script.

Decentralization has been on the rise lately, 2018 being a huge year when it came to software adopting its principals. With Carthage created in 2014, it could be said it was an early adopter in the software world, though decentralization in computers had been popular for quite some time, especially in cryptocurrencies. Carthage is a decentralized package manager, meaning there is no core server nor central list of projects. Instead, the package manager reaches out to various GitHub repositories to fetch the code to be built into frameworks. While this does complicate the locating of Carthage-compatible frameworks, it does mean that Carthage is more secure, and takes advantage of a concept developers are beginning to love. Also, it does mean that Carthage can be slower than Cocoapods while reaching out for and building dependencies, but it’s the developer’s choice if the trade-offs are worth it.

Carthage is revolutionary in how it carries out package management, and those it can be complex at first, it does serve as a prominent package manager, allowing developers to take control of their dependencies.

The Swift Package Manager is the newest to join the ranks of the Swift package managers, being introduced on September 13th, 2016 with the release of Swift 3. The Swift Package Manager more strongly resembles its counterparts in other languages than Swift’s other package manager, making it easier to pick up for those coming from another language. It adds a file structure to your project that would make anyone coming from Cargo or NPM feel at home, organizing each dependency into its own folder representing the module. It is equally as simple to set up for development for iOS, Mac, and Linux, a perk that the other package managers seem to lack. The manager’s manifest also resembles that of other languages, with a specific JSON-like structure.

The Swift Package Manager is new, built into the Swift language (so no need to install anything!) and always freshly updated for the latest versions of Swift and Xcode almost as soon as they are released. Since it was built with Swift 3 originally and is frequently updated, it avoids the issues of legacy code and the need to update as frequently, something other package managers need to do. The command line interface integrates directly into Xcode and the Swift build process, meaning there is no additional configuration to do outside of the Package.swift file to declare dependencies.

The Swift Package Manager bears an important signature that Cocoapods and Carthage lack: Apple’s stamp that it is Swift’s official package manager. Developed in the open at Apple with contributions from the community, it shares the open source aspect that is popular with developers and is used by Swift’s other package managers. Apple’s backing and having their team working with core development gives the Swift Package Manager a boost of efficiency and credibility, meaning that developers have their build processes accelerated and simplified while knowing that they’re building dependencies using the best package management software available on Mac or Linux for the Swift language. Though some developers are not huge fans of Apple and their policy and methodology, it is hard to deny that the Swift Package Manager is a great creation of theirs.

All notable programming languages, and many that developers likely have never heard of, have their own package managers to allow for the distribution of third-party frameworks and libraries to the development community. Cocoapods is different than other package managers for its use of another language and total project manipulation, and Carthage is different for its control given to the developer and decentralization. The Swift Package Manager’s formation is similar to its counterparts in other languages, meaning that a developer who has ever dealt with dependencies will feel comfortable with it. The commands in the CLI are similar to the likes of other package managers, and the Package.swift file is very similar to Kotlin’s Manifest.json or JavaScript’s Package.json, and the structure the files take in the project they are being added to is near identical to the structure they take in other languages.

The Swift Package Manager is looked upon as the future of dependency management in Swift projects, and its efficiency and simplicity are doing well to pave the way towards this prediction.

Three proficient package managers in one languages can be a burden for developers, whether they be choosing the right one or developing packages to be used in other’s projects, distributed through the manager. Swift’s dilemma not only includes the diversity in dependency management, but that each package manager is far from perfect and can leave much to be desired. Cocoapods users wish it wasn’t written in Ruby and that it didn’t manipulate their project as much, Carthage users wished the process wasn’t as complex or slow at times, and users of the Swift Package Manager wished it wasn’t as obscure or cut-and-dry with other language’s package managers. On the positive side, Swift’s package management system, though it be far from perfect, allows developers to have a choice, something that is not as easy or not available at all in other languages.

Afterword

So, what is Swift’s package management dilemma? It is that Swift has multiple package managers, each with their triumphs and shortcomings, and that there are many factors to be considered when choosing one or extra steps required to be taken when developing libraries and frameworks for others to use. Though it may be a dilemma, Swift’s multiple package managers each contain aspects different from their counterparts in other languages, making the dependency building and implementation process in Swift unique and powerful in its own way.