DIY Swift Package Manager Dependencies
The way we manage iOS dependencies has evolved a lot over time. And the latest and most integrated offering is the Swift Package Manager (SPM).
But what to do if your favorite dependency is not SPM-compatible (yet)? We do it ourselves!
Table of Contents
A brief history
In the beginning, there was nothing.
As with all software projects, relying on dependencies and sharing code is a crucial key for a better developer ecosystem.
But sharing code meant we need either to copy the source directly into your project or linking it with a framework manually.
About three years after the original iPhone SDK was released, the first dependency manager was released, too.
But not by Apple.
CocoaPods
CocoaPods is an application level dependency management system inspired by RubyGems.
Installable with already available tools in macOS, creating a single extra file called Podfile
with all your dependencies, and running pod install
.
That’s all that’s needed to get our favorite 3rd party frameworks automagically integrated into our Xcode project by creating an Xcode workspace, and adding them as standalone projects linked to our original project.
CocoaPods is source-based, so all our pods are compiled with our project. This means longer compile times, and possible breakage on SDK updates. Also, by re-working your project structure to a workspace it’s tightly integrated now and not easily removable.
But it was the status-quo of dependency management for iOS projects, so we learned to love it, and it worked very well, and grew fast.
Carthage
The tight integration of CocoaPods is also a caveat. A more loosely coupled solution emerged: Carthage.
No touching of our project files. No additional projects. No extra compilation every time we compile your project. No centralized repository.
Instead of integrating the dependencies as source code into your project, Carthage downloads the source, compiles it only once, and we have to add the framework manually. Yes, it’s more work as with CocoaPods. But it’s also more flexible and not as tightly coupled.
Swift Package Manager (SPM)
It’s a shame Apple didn’t provide a solution for dependency management in Xcode for 11 years. Finally, Xcode 11 got a native integration of the Swift Package Manager that was released 3 years earlier.
We can add our dependencies directly within Xcode, and the Swift Package Mangager will automatically download, compile, and link all the dependencies for us.
Just like Carthage, it doesn’t need a central repository. Any Git repository can be used, even local ones.
The biggest problem is that not all dependencies are compatible yet. But we can change that ourselves.
Non-SPM to SPM
In my current iOS project, I’m trying not to mix different dependency managers. We could use all of them together, but this means we have to deal with their edge cases and problems at once.
Instead of using CocoaPods, the project was started with Carthage. With the release of Xcode 11 I’ve migrated all but one dependency to SPM.
A few days ago I wanted to add another dependency for easier “empty state” handling of UICollectionViews. But the 2 libraries I wanted to try out were both not SPM-compatible.
Of course, I could have added them to a Cartfile
and use them with Carthage, but what’s the fun in that?
Instead, I decided to make them compatible with SPM myself.
Package.swift
The core of every SPM dependency is a file called Package.swift
.
It tells SPM how to consume the dependency by specifying source folders, targets, version requirements, etc.
With the simple addition of a Package.swift
file we’ve created an SPM-compatible dependency.
Here’s an example:
Of course, there are many other options. But for simple pods or Carthage-based dependencies, this might be just enough to get it up and running.
Here is all the information about the SPM and how to use it if you’re interested in the rest of the options:
Hosting the dependency
The simplest way to host our modified dependency is a GitHub fork.
- Fork the original dependency
- Checkout your fork
- Add Package.swift as needed
- Add, Commit, Push.
- Done!
Now we can use the dependency as any other dependency. With the additional advantage of creating a pull request with our changes to the original dependency, so others might enjoy an SPM-compatible dependency. Or we merge changes back into the fork if the original author is not interested in adding SPM for free.
As mentioned before, we could also use a local project by specifying the git repository with a file:///Users/ben/code/path/to/repository/
Caveat
At the time of writing the article, the SPM doesn’t support anything else but code. No resources or bundles.
So if your dependency needs any assets, you’re out of luck at the moment.
For the dependencies I needed, this wasn’t a problem, but your requirements may differ.
Conclusion
Using Swift Package Manager is great, and simple dependencies can easily be converted by ourselves.
In my opinion, you should try to use SPM if possible because it’s the native and kind of official way to go.
If you need other dependencies that can’t be converted, you should rely on Carthage. But I’m really looking forward to only needing a single dependency manager in the future.