Productivity is all about knowing your tools, and knowing which tool to use for the job. But do to so we need to invest time into experimenting with other tools other than the one we are comfortable with. Even if the experiment turns out to be unsuccessful, by going outside our comfort zone we learn to learn, which is probably the single most important thing that a software developer can do.
For what seems like a long time, but is actually just a couple of years, CocoaPods has been the tool that I and many other developers have used to manage dependencies in iOS and OS X projects. Recently, with the advent of Swift an new tool has entered the scene: Carthage.
In this post we will look at how to use Carthage to install testing dependencies on an Xcode 7 + Swift 2 project. The testing framework of choice is Quick, together with Nimble to write cleaner assertions. You can find the code for this example on GitHub, as usual.
Carthage
Carthage is a very interesting dependency manager. It is written in Swift and does massive use of ReactiveCocoa, and its approach is focused on simplicity. Where CocoaPods does everything for us, Carthage only resolves, downloads, and -when necessary- builds dependencies, leaving us the responsibility of adding them to the project, using the methods and settings we find more appropriate.
You can install Carthage using Homebrew:
brew install carthage
The Cartfile
The way to specify the dependencies in Carthage is with a Cartfile
. This is how our looks like:
github "Quick/Quick" "v0.5.0"
github "Quick/Nimble" "v2.0.0-rc.1"
Note how we are specifying exact version numbers. This is because Swift 2 support has been added only in those version, and being all a work in progress we want to make sure we use a stable version.
The Cartfile supports the usual operators for version requirements such as ~>
, >=
, etc. You can read more about all the valid options here
Getting the frameworks
Resolving the dependencies, download the right versions, and finally build them is as easy as running a single command:
carthage update
But since we are installing frameworks using Swift 2, we need to switch the sdk used by xcodebuild
, which is call by carthage update
under the hood, to the Xcode 7 one, like this:
sudo xcode-select --switch /Applications/Xcode-beta.app/Contents/Developer
carthage update
Once carthage update
has finished you will notice a Carthage
folder, and a Cathage.resolved
file. Remember to check-in to version control at least the Carthage.resolved
file.
It's now time to import the frameworks into Xcode. Because we are only setting testing depencendcies we should add the to the test target only. To do that need to follow a different approach than the usual one. Quoting from Carthage's README:
Because non-application targets are missing the “Embedded Binaries” section in their build settings, you must instead drag the built frameworks to the “Link Binaries With Libraries” build phase.
Just another bit of setup
When building for iOS, due to an App Store bug we need to add a "Run Script" Build Phase, that will execute a script to work around the issue. This is the Run Script's content:
/usr/local/bin/carthage copy-frameworks
And these are the paths to our frameworks:
$(SRCROOT)/Carthage/Build/iOS/Quick.framework
$(SRCROOT)/Carthage/Build/iOS/Nimble.framework
You can read more about the process here.
After that, we need to add the Carthage's build folder ($(SRCROOT)/Carthage/Build/iOS
) to Framework Search Path, in recursive mode. This is apparently due to a regression (or is it a feature?) introduced by Xcode 7, more here.
Once that is done we can happily import our testing frameworks in the test target:
import Quick
import Nimble
class QuickNimbleCarthageSpec: QuickSpec {
override func spec() {
describe("Setting up Quick and Nimble for testing using Carthage") {
it("is not very hard") {
expect(true).to(beTruthy())
}
it("works very well") {
expect(20 * 2 + 3 - 1).to(equal(42))
}
}
}
}
Protip: Make them private
Since the frameworks used in the testing target do not concern the main one there is no reason to make their dependencies known to the parent project. To do this we simply have to declare those dependencies in the Cartfile.private
file. Quoting from Carthage's docs:
mv Cartfile Cartfile.private
Frameworks that want to include dependencies via Carthage, but do not want to force those dependencies on parent projects, can list them in the optional Cartfile.private file, identically to how they would be specified in the main Cartfile.
Anything listed in the private Cartfile will not be seen by dependent (parent) projects, which is useful for dependencies that may be important during development, but not when building releases—for example, test frameworks.
I hope you found this post useful, and that you will consider trying Carthage in your next project. As always we should try to be pragmatic with our tools and always look for the best fit for the job. But to be able to take and informed decision there is only one way, getting exposure to as many different solutions as possible.
Remember to checkout the example project on GitHub.
If you have feedbacks, suggestions, or want to report a mistake leave a comment below, or tweet me @mokagio.
Happy coding, and leave the codebase better than you found it.