What’s the perfect dependencies manager for iOS

Our journey through CocoaPods, Carthage, and Swift Package Manager

Mateusz Kuźnik
Mar 31 · 7 min read

Azimo’s iOS application went through many steps and turnovers over time. Some of the solutions that served us well in the past became obstacles in development speed, quality of our product, and growing codebase. This post discusses one of these experiences — working with dependencies managers, CocoaPods, Carthage, and Swift Package Manager, and migrating our project between them.


When we started our iOS app development, Swift language wasn’t stable enough to use in the commercial product. Back then, CocoaPods was a default option for the majority of iOS projects. So it was for us.

CocoaPods brings a lot of simplifications into dependencies management:

  • Dependencies are managed from a single place. You don’t have to download them from their websites or repositories.
  • Thanks to Podfile, your teammates don’t have to search through the project to check which dependencies are installed.
  • Some of the dependencies have their own dependencies. CocoaPods take care of figuring that out for you.
  • Updating dependencies is much simpler. Just bump version name in a Podfile, run pod install in a terminal, and you are home.
  • CocoaPods is a well-known solution across the iOS engineering world. Most of your questions are already answered. Even now, CocoaPods remains the default dependency manager for engineers who are creating their libraries and SDKs.

Here’s how to start with CocoaPods.

Podfile with:

platform :ios, '10.0'

target 'MyApp' do
pod 'AFNetworking', '~> 4.0.1'
pod 'Firebase/RemoteConfig', '~> 7.9.0'

and terminal command:

$ pod install

This is all you need to start using it in your project. For the exact setup, visit https://cocoapods.org/.

CocoaPods simplicity comes with a cost that became more visible when your project grows bigger. Here are some of the challenges that forced us to consider migration into a different solution:

  • Adding non-public/internal frameworks requires extra work with Private Spec Repo. Because some of our dependencies weren’t publicly available and others are our own internal libraries, we needed to spend some time modifying .podspec files.
  • CocoaPods makes changes in the project’s configuration file project.pbxproj. If something goes wrong, it can affect the building process. Debugging takes a lot of time and requires understanding the changes that CocoaPods introduced under the hood.
  • When you decide to remove a framework, not all of the changes are cleaned up. Over the years, these leftovers stack up.
  • CocoaPods require a Ruby environment. It’s another thing that you need to keep in sync with all of the machines.


A couple of years ago, we decided to migrate our dependency management from CocoaPods to Carthage because of the things mentioned above. For a startup company, the early years mean a lot of experimentations and pivoting. During our journey, we introduced many changes in 3rd party solutions within a short time. Constant changes in libraries resulted in a growing mess in our project configuration file. The stability of our CI/CD pipelines decreased too.

After the migration, Carthage solved some of our problems. Here are the pros of this tool that made our engineer’s life easier:

  • Adding new dependency doesn’t change your project configuration at all. Carthage builds dependencies using xcodebuild and doesn’t touch project.pbxproj file. It also means that it’s now the developer’s responsibility, but it was a good thing for us. It brought us better control over our app’s codebase and simplified debugging process when something went wrong.
  • As contrary to CocoaPods, Carthage builds dependencies only once, during the resolution process. They are then added to the project as a framework, so we don’t have to rebuild them each time.
  • Private repositories don’t require any additional setup. Carthage can point out git repositories and get sources from there.
  • Carthage doesn’t require a Ruby environment (and any other one). It can be installed via Homebrew or as a .pkg file. No more version sync between your teams’ computers and CI/CD. 👍
  • When it comes to CI/CD, there is also a great caching solution that we have been heavily using to share dependencies between our pipelines. It’s called Rome.

While the decision about migrating our project to Carthage was an excellent move for us, this tool also has some cons:

  • In our case, It took a lot of time to resolve dependencies. In the best-case scenario (everything’s cached), it’s a couple of seconds. But In the worst case, from 5 to even 35 minutes 😱 (no cache, full resolution). It can be painful if you are working on an internal framework and the app simultaneously. Each change in a framework needs to be pushed to the repository and resolved on the app’s side. (It is possible to make changes locally, but the solutions aren’t straightforward, yet the dependencies still need to be resolved as usual).
  • Carthage builds all of the schemes in each project, even if it is not required. Some projects contain a demo app (sometimes even more than one) with a shared scheme. Because of them, building time adds up.
  • Additional Build Phase for each library is required.
  • We had a hard time when doing the project’s migration to Xcode 12. Since the Apple Silicon introduction, Carthage made our compilation failing in a nasty way. You can find more details and the solution for that here.

Swift Package Manager

The challenges mentioned above (especially resolution time) made us consider using another dependency manager solution, Swift Package Manager. We are in the middle of the migration process between Carthage and SPM, yet even now, results are promising.

The things which SPM makes better in the context of our project are:

  • It has better support for the internal frameworks. To work on a framework and the app simultaneously, we just need to drag and drop the first one into the Xcode project’s navigator.
  • As contrary to Carthage, SPM builds what is required (no more rebuilds of demo projects) and doesn’t need an extra step in Build Phase,
  • Because of Apple’s official support, we hope to have no more issues when moving to a newer version of Xcode. 🤞🏻
  • After migrating from CocoaPods to Carthage, we removed the Ruby framework. With SPM, we don’t require any additional installation (which was the case for Carthage). The entry-level of our project is even lower.
  • It also means fewer steps to perform in Terminal to resolve all dependencies. Everything is part of Xcode now.
Adding dependency in SPM is just drag’n’drop 👍

We also started noticing the global industry trend of migrating into this SPM dependency manager. Some of our partners have already dropped support for Carthage in favor of Swift Package Manager.

Of course, SPM isn’t perfect. Even if we think that it’s now better than CocoaPods and Carthage, there are still some challenges to keep in mind. The biggest ones for us are:

  • Currently, caching mechanisms seem to be problematic, especially in the context of CI/CD. Xcode has its internal cache, which isn’t publicly described. It raises a question mark around how it’ll work if CI/CD runs multiple pipelines which use different versions of dependencies.
  • Not all frameworks support SPM. Our ecosystem isn’t there yet.
  • And close to the one above — we still face problems, especially with frameworks that need to be used in the Objective-C code. These don’t look like SPM’s problem, yet made us having some difficult times.

How to migrate your project from Carthage to Swift Package Manager

Here is our internal, step by step instruction for how you can migrate each framework from Carthage to SPM:

  1. Select the framework you want to migrate
  2. Remove the framework(s). Please, have in mind that one framework can have multiple products:
    • Find the framework in the navigator.
    • Delete it.
  3. Remove the framework from the Cartfile file.
  4. Remove the framework from the Cartfile.resolved file.
  5. Remove the framework from the Carthage.xcfilelist.
  6. Remove the framework from the Carthage’s PhaseScript for each target if needed:
    • For most of the frameworks, this step should is not required because of Carthage.xcfilelist file.
    • In the Navigator, select the main project.
    • In the Targets section, select a target you want to include in the new framework.
    • Select the Build Phases tab
    • Select the Carthage section and remove the framework
  7. Remove the framework from the Romefile file.
  8. Add a new SPM entry:
    • For most cases, we want to use the Exact version and update them manually.
    • Select product(s) and a target to which the entry should be added.
    • Now we can choose only one target. We will add it to others later.
    •Add the new framework to the required targets if needed.
    • In the Navigator, select the main project.
    • In the Targets, section selects a target you want to include in the new framework.
    • Select the Build Phases tab
    • Add all required frameworks to Link Binary With Libraries.
  9. To test the integration:
    • Clean project or empty the DerivedData folder
    • Build and Run project
    • Archive project

Towards financial services available to all

We’re working throughout the company to create faster, cheaper, and more available financial services all over the world, and here are some of the techniques that we’re utilizing. There’s still a long way ahead of us, and if you’d like to be part of that journey, check out our careers page.


We’re the tech team behind Azimo — the faster, cheaper way…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store