Dependency Managers: Carthage

      No Comments on Dependency Managers: Carthage

Carthage is a decentralised dependency manager for iOS/OSX. In this post, we’ll see how you can declare your project Carthage compatible and how to use Carthage in your projects.

Carthage vs Cocoapods

Carthage is a decentralised dependency manager. What this means is that, unlike Cocoapods, it does not rely on a central repository. Carthage, much like Cocoapods, has a file where you define your dependencies called a ‘Cartfile’. You would list your external frameworks/modules in this file, but you would also provide a link to a git repo. So when you run ‘carthage update’ Carthage will check out the frameworks from git. Unlike Cocoapods, it does not rely on a central repository.

Cocoapods has a central repository of all the podspecs and in order to use Cococapods you need a local copy of this repository (called the ‘Specs’ repository), this is exactly what makes Cocoapods centralised. One major downside of this is the size of the ‘Specs’ repo, and the size keeps growing with every new pod added to the repo. Also, you have one repo with all the podspecs, which means, a single point of failure. If that repo goes down, you won’t be able to use Cocoapods. Carthage does not suffer from these problems and is very flexible.

One more difference between the two is that Cocoapods will create a workspace for your project, while Carthage won’t touch your project/workspace. So if you already have a complex project in place, your only option might be Carthage.

Installing Carthage

We could complicate things, but I like to keep it simple. And so do guys at Carthage, apparently. Just go to Carthage release page, download the latest ‘Carthage.pkg’ file and install it. That’s it 🙂 To check if you have it installed correctly, open the terminal and type in:

dejan$ carthage version
0.22.0

If you can see the version printed below and no errors, you have Carthage installed, congratulations 🙂

Declaring Compatibility

It might seem weird that I would talk about this before talking about using Carthage, but It’s incredibly simple to do this, I just want to get it out of the way 🙂

I’m going to make one of my GitHub projects Carthage compatible, DASorting. First off, make sure your schema is shared. Click on your target, then on ‘Manage Schemes…’ like so:

And make sure you have the ‘Shared’ checkbox ticked for your project:

Believe it or not, that’s pretty much it, project wise. To make sure everything actually works, open the terminal, go to your project directory and type in ‘carthage build –no-skip-current’:

DejansMacBookPro:DASorting dejan$ carthage build --no-skip-current
*** xcodebuild output can be found in /var/folders/gw/1f9ftf8s2wxbk6t0r89znfh80000gn/T/carthage-xcodebuild.Ga3hr1.log
*** Building scheme "DASorting" in DASorting.xcodeproj

After running the command, go to Carthage/Build folder and make sure your .framework is there. If it’s there, it means your project is now Carthage compatible.

Carthage is using semantic versioning, much like Cocoapods, so be mindful of that. One last step you need to do is to create a tag. That’s how Carthage knows which version of the framework to use, so let’s do that. I’m using Sourcetree, so I’ll go to my ‘Repository’ menu and select ‘Tag…’ from it:

You’ll see your tag window, give your tag a meaningful name, and make sure ‘Push tag:’ is checked:

Let’s just double check and make sure our tag is there. I’m using GitHub for this repo, so if I select my branch dropdown and select tags, I can see my tag there:

Sweet. Now let’s be a good open source citizens and declare to others that we support Carthage. We can add some markup to our Readme.md file, like so:

[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)

This will give us a nice little badge on our GitHub:

Now let’s see how to actually use this in a project.

Using Carthage

Carthage is using a ‘Cartfile’ to resolve it’s dependencies, so go ahead and create one, without any extension.

Carthage currently supports projects hosted on GitHub, Git, and binary frameworks. You can even use a local git project. For the git projects, you can specify the versions you want to use or even branches.

My Cartfile looks like this:

github "dagostini/DASorting"

Now we’ll try to pull the dependency into our project, so let’s run ‘carthage update’:

DejansMacBookPro:DACarthageExample dejan$ carthage update
*** Cloning DASorting
*** Skipped downloading DASorting.framework binary due to the error:
	"Must specify two-factor authentication OTP code."
*** Checking out DASorting at "0.1.0"
*** xcodebuild output can be found in /var/folders/gw/1f9ftf8s2wxbk6t0r89znfh80000gn/T/carthage-xcodebuild.tuQa98.log
*** Building scheme "DASorting" in DASorting.xcodeproj

You can ignore the error you see in my output, and head out to ‘Carthage/Build’ folder and find DASorting.framework:

For each framework that you use you will have to add it to your project manually. So go to your project ‘General’ tab and drag and drop this framework under ‘Linked Frameworks and Libraries’, you should see something like this:

You’ll have to add a ‘Run Script Phase’. In your ‘Build Phases’ click on the plus icon and select ‘New Run Script Phase’:

Now, you’ll have to call a script that copies frameworks when you archive your app. Under ‘Input Files’ you’ll need to add paths to all the frameworks that you’re using:

There we go, we’re all set. If you want to update your frameworks you just run ‘carthage update’ from the console.

We’re using ‘DASorting’ for demo here, so let’s see how we can use our framework from withing the demo project that we have setup here. Let’s quickly test it out in the AppDelegate. We’ll have to import our ‘DASorting’, and we’ll just access one of the array extensions to prove everything works as expected:

import UIKit
import DASorting

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        testSorting()
        
        return true
    }
    
    private func testSorting() {
        let test = [1, 7, 8, 4, 9, 2, 3]
        let results = test.mergeSorted()
        
        print("Sorting results: ", results)
    }
}

Now we have everything setup and we’re using Carthage in our project successfully.

When you first ran carthage update ‘Cartfile.resolved’ was generated for you, it’s important that you commit this file to your repo because you want other members of your team to be able to checkout the repo and use carthage on it. They would simply have to run ‘carthage bootstrap’ command from the terminal to fetch the frameworks.

Conclusion

We covered the main use case of Carthage in this post. One of the other cool features that we didn’t cover here is the ability to use precompiled frameworks in Carthage. This is really cool, you could attach a file to your release on GitHub, or host a framework on your server and Carthage would be able to use it. If you’re building a framework and don’t want people to have access to your source code this might be an attractive option.

Carthage definitely requires a few extra steps to setup, compared to Cocoapods, but the flexibility it offers makes this worth a while. And, let’s face it, you don’t add new frameworks/libraries to your projects on a daily basis. You add them when you really need to 🙂

You probably want me to answer your question ‘Which dependency manager should I use?’ and I’ll annoy you by saying: ‘Depends on the project you’re working on!’. Sorry about that. If you’re starting a new project and you want simplicity, go ahead with Cocoapods. If you want flexibility, then go ahead with Carthage. If you already have an existing project and you’re using workspaces, then Carthage would be a better option (between the two, the only option). If you foresee your project becoming very complex in the long run, Carthage is the way to go. I’ve seen projects that are using workspaces and a lot of subprojects, in this case, Carthage is the best option to use. One more important factor in your decision might be the decentralised nature of Carthage. Unlike Cocoapods it doesn’t have a single point of failure, the ‘Specs’ repo. If the guys at Cocoapods decide to delete the ‘Specs’ repo, Cocoapods will become useless, it’s as simple as that. Carthage doesn’t suffer from this problem. It’s built from the ground up to be decentralised.

In one of my previous posts I was talking about Cocoapods and how to create your own pod, check it out if you want. I’m not trying to sell you one solution over the other, I’m just providing you with options. It’s up to you to decide which dependency manager suits you best. I hope I helped you just a bit and that you found this article useful. As always, you can find the example project on my GitHub account, I used one of my other libraries (DASorting) as a Carthage compatible framework, you can find it here.

Have a nice day 🙂

Dejan.

More resources

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.