iOS Dependency Management
Overview
In 2022, the majority of large-scale production apps use 3rd party libraries. Companies want to ship new features, and do not want to re-invent the wheel when it comes to common things like animations, networking, or even some common views apple may not give us. 3rd party libraries are everywhere! But as your app grows, it can get pretty nasty dealing with all the different libraries, and keeping the versions in line with your dependency tree. Another big advantage of dependency managers is backwards compatibility. If a framework updates its code, you can still support older versions of the framework without having to change anything in your project.
With some open source frameworks you could statically import all the code/dependency versions you need by hand, but that is unneeded work! The iOS ecosystem has evolved enough to give us several ways of managing our 3rd party dependencies in our app. Let's take a look at how to set up each one, and the pros/cons of each.
Semantic Versioning
I would be remiss to not mention semantic versioning here. All of the dependency managers here will likely use some type of semantic versioning when it comes to listing the dependencies of our project. The versions of dependencies are defined by MAJOR, MINOR, and PATCH versioning numbers in the form of 1.2.3 . As you can guess, big code changes are MAJOR, medium code changes are MINOR, and small fixes are patches. Using semantic versioning, we can define greater than, less than, equal to, compatible with, etc versions for 3rd party libraries!
CocoaPods
Feel free to visit the cocoapods website if you want to learn about all the different pods that are available for iOS.
If you do not have cocoapods installed on your machine, feel free to run the following command in terminal:
sudo gem install cocoapods && pod setup --verbose
This will install cocoapods on your machine. Next you will want to cd into the directory where your xcode project lives, and run:
touch Podfile
Which will create a Podfile for your project. A Podfile just describes the dependencies of the targets of one or more Xcode projects. Next you will want to open up the Podfile with your favorite text editor and tell the Podfile you want to use frameworks for your project. It should look like this:
platform :ios, '16.0'
target '<Your Target>' do
use_frameworks!
end
This tells your project to use frameworks instead of static libraries. Reminder that frameworks allow you to bundle images & assets, while static libraries do not. Now time to add a Pod (framework)! Change your Podfile to this:
platform :ios, '16.0'
target 'CocoaPods' do
use_frameworks!
pod 'Alamofire', '4.9.1'
end
This will add the Alamofire Pod at version 4.9.1 to our project. In the terminal run pod install to finalize. Now make sure you open the newly created .xcworkspace in your project directory, and pick a file. You should be able to build & then import Alamofire!
Carthage
Similar to Cocoapods, Carthage is a 3rd party dependency manager...with a little different of a setup.
Lets start with installation. Go ahead and run brew install carthage in the terminal.
Similar to Cocoapods we will create a Cartfile, allowing us to list all of our dependencies in our project.
touch Cartfile
and then open your newly created Cartfile with whatever text editor you prefer. Now add the following line to the Cartfile:
github "Alamofire/Alamofire" == 4.9.0
This specifies the exact version of Alamofire we want for our project. We can also use ">=" to specify a version greater than or equal to a semantic version number. OR we can use "~>" specifying Carthage to use 'Use any version that’s compatible with X'. We can also specify branches of the library or git Hash commits to use. Very powerful!
Now in our terminal enter the following:
carthage update --platform iOS
Next we can see what Carthage has built & checked out for us by entering open Carthage in terminal. This is where Carthage differs from cocoapods a little bit. With Carthage we need to manually integrate the Binary Frameworks into our project. We can do this by going to our Target in Xcode, and scroll to the Linked Frameworks and Libraries section. Drag the Alamofire.framework from the Carthage directory into this section in Xcode. This links those frameworks to your project.
Easy Peasy!!
Swift Package Manager
And now it is time to introduce you to my personal favorite dependency manager, SPM or Swift Package Manager. Swift can divide code into "Packages".
Lets say we have a project in which we have to decipher some Yaml files. We could build this ourselves from scratch, or we could use an existing Library!
In Xcode go to File > Add Packages and enter the URL of the package. In our case we will be using one hosted on Git, with the URL of https://github.com/jpsim/Yams.git . We can define any versioning we want for this package. Also, SPM automatically links the package to our project target! Neat!
Packages are very powerful because they keep our code extensible and modularized. If we have a grand scale application, we can break it down by feature, and each feature can have its own Package manifest file if we so desired. The scalability benefits here are very evident.
As you can see, the ease of use & Xcode integration make SPM a fan favorite!
Tradeoffs
So now we have touched on SPM, Cocoapods, and Carthage. What are some of the pros and cons of each?
Cocoapods:
Pros:
- Supports both Dynamic Frameworks and Static Libraries
- Automatic dependency tree resolution
- Wide range of availability and documentation
- Has an official mac app to manage your dependencies
Cons:
- Slower build times, because every time you compile, your dependencies get built as well
- When you install your first dependency, the main specs repo gets downloaded, and it takes a very long time
- Your main project will be altered to be able to use all of your 3rd party dependencies
Carthage:
Pros:
- Supports both Dynamic Frameworks and Static Libraries
- Automatic dependency tree resolution
- Faster than Cocoapods because your frameworks only get built once, on the first time you call "carthage update"
- Your project remains untouched, unlike Cocoapods
Cons:
- Little steeper learning curve, and more steps to actually add a dependency
- Limited framework capacity. Not everyone supports Carthage
SPM:
Pros:
- The Apple standard, on how to build and distribute code
- Works on Linux
- Less steps to bootstrap and get started
Cons:
- You have to follow a specific directory structure structure
- Swift Only!