Carthage Tutorial: Getting Started

In this Carthage tutorial, you’ll learn what Carthage is, how to install it and how to use it to declare, install and integrate your dependencies. By Felipe Laso-Marsetti.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Avoiding Problems With Your Artifacts

Whether you commit the Build and Checkouts directories to your version control repository is up to you. It’s not required, but doing so means that anybody who clones your repository will have the binaries and source for each dependency available.

Having this backup can be a useful insurance policy if GitHub is unavailable or a source repository is removed.

Don’t change any code inside the Checkouts folder because a future carthage update or carthage checkout command may overwrite its contents at any time. Your hard work would be gone in the twinkling of an eye.

If you must modify your dependencies, run carthage update using the --use-submodules option.

With this option, Carthage adds each dependency in the Checkouts folder to your Git repository as a submodule, meaning you can change the dependencies’ source, and commit and push those changes elsewhere, without fear of an overwrite.

The bootstrap command downloads and builds the exact versions of your dependencies specified in Cartfile.resolved. carthage update would update the project to use the newest compatible versions of each dependency, which may not be desirable.

Note: If other developers use your project and you haven’t committed the built frameworks with your code, they’ll need to run carthage bootstrap after checking out your project.

The bootstrap command downloads and builds the exact versions of your dependencies specified in Cartfile.resolved. carthage update would update the project to use the newest compatible versions of each dependency, which may not be desirable.

Now, how about actually using these build artifacts you worked so hard to create? You’ll do this in the next section.

Adding Frameworks to Your Project

Back in Xcode, click the DuckDuckDefine project in Project navigator. Select the DuckDuckDefine target. Choose the General tab at the top then scroll to the Frameworks, Libraries, and Embedded Content section at the bottom.

In the Carthage Finder window, navigate to Build/iOS. Drag both Alamofire.framework and AlamofireImage.framework into the Linked Frameworks and Libraries section in Xcode:

Adding AlamoFire frameworks to Xcode project

This tells Xcode to link your app to these frameworks, allowing you to make use of them in your code.

Next, switch to Build Phases. Click the + icon in the top-left of the editor and select New Run Script Phase. Add the following command to the block of code under Run Script:

/usr/local/bin/carthage copy-frameworks

Click the + icon under Input Files and add an entry for each framework:

$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework
$(SRCROOT)/Carthage/Build/iOS/AlamofireImage.framework

The result looks like this:

Adding a run script phase for the Carthage frameworks

Strictly speaking, this build phase isn’t required for your project to run. However, it’s a slick workaround for an App Store submission bug where apps with frameworks that contain binary images for the iOS simulator are automatically rejected.

The carthage copy-frameworks command strips out these extra architectures. WOOt!

Although there won’t be anything new to see yet, build and run to ensure everything’s working as expected. When the app launches, you’ll see the search view controller.

An empty dictionary app

OK, great. Everything looks good. Next, you’ll work on upgrading dependencies.

Upgrading Frameworks

I have a confession to make.

Remember when you created your Cartfile earlier and I told you which versions of Alamofire and AlamofireImage to install? Well… I gave you bad information. I told you to use an old version of Alamofire.

Don’t be mad though! I did it with the best of intentions. Look at this as an opportunity to learn how to upgrade a dependency.

Open your Cartfile again. From your project’s directory in Terminal, run:

open -a Xcode Cartfile

Change the Alamofire line to:

github "Alamofire/Alamofire" ~> 4.9.0

As you saw earlier, this code instructs Carthage to use any version of Alamofire that’s compatible with 4.9.0. This includes any version up to but not including a future 5.0 version.

When adding dependencies with Carthage, you want to consider compatibility and limit the version you target. That way, you know the exact state of its API and functionality.

For example, version 5.0 of a dependency might include app-breaking API changes. You wouldn’t want to automatically upgrade to that version if you built your project against 4.9.0.

Save and close the Cartfile and return to the terminal. Perform another update:

carthage update --platform iOS

This tells Carthage to look for newer versions of each of your dependencies. It will then check them out and build them, if necessary. In this case, it fetches the latest version of Alamofire.

Because your project already contains a reference to the built .framework for Alamofire, and Carthage rebuilds the new version in the same location on disk, you can sit back and let Carthage do the work. Your project will automatically use the latest version of Alamofire!

Duck, Duck… GO!

Now that you’ve integrated Alamofire and AlamofireImage into the project, you can put them to use by performing some web searches.

In Xcode, open DuckDuckGo.swift. At the top of the file, add the import below:

import Alamofire

Next, replace the contents of the existing performSearch(for:completion:) with this:

// 1
let parameters: Parameters = [
  "q": term,
  "format": "json",
  "pretty": 1,
  "no_html": 1,
  "skip_disambig": 1
]

// 2
Alamofire.request(
  "https://api.duckduckgo.com",
  method: .get,
  parameters: parameters)
  .responseData { response in
    // 3
    guard 
      response.result.isSuccess, 
      let jsonData = response.result.value 
      else {
        completion(nil)
        return
    }
    
    // 4
    let decoder = JSONDecoder()
    
    guard 
      let definition = try? decoder.decode(Definition.self, from: jsonData),
      definition.resultType == .article 
      else {
        completion(nil)
        return
    }
    
    completion(definition)
}

Here’s what this does:

  1. First, you build the parameters to send to DuckDuckGo. The most important two here are q, the search term itself, and format, which tells the web service to respond with JSON.
  2. Then you perform the request using Alamofire. This call makes a GET request to https://api.duckduckgo.com, using the parameter dictionary created above.
  3. Once the response comes back, check if the request failed and optionally bind the JSON response object to ensure it has a value. If something is wrong, exit early.
  4. Next, use JSONDecoder to deserialize the Definition, which conforms to Codable. The DuckDuckGo API can return a range of different result types, but the one covered here is Article, which provides a simple definition of the search term. You filter for article then pass the retrieved definition to the completion handler.

Disambiguation results are like this Christopher Evans page on Wikipedia that clarifies whether you meant Chris Evans the movie actor, Chris Evans the British TV personality or Chris Evans the train robber.

skip_disambig means the API will pick the most likely result and return it.

Note: If you’re wondering why the skip_disambig parameter exists, it’s to tell DuckDuckGo not to return ‘disambiguation’ results.

Disambiguation results are like this Christopher Evans page on Wikipedia that clarifies whether you meant Chris Evans the movie actor, Chris Evans the British TV personality or Chris Evans the train robber.

skip_disambig means the API will pick the most likely result and return it.

Build and run. Once the app starts, enter “Duck” in the search bar. You’ll now see a definition on the next screen.

Performing a search for Duck in Duck Duck Go

There’s something missing, however: a picture! It’s one thing to read what a duck is, but who reads anymore? Pictures are worth — okay, I’ll spare you the cliché, you know what I mean.

Anyway, who doesn’t like looking at pictures of ducks? Kittens are so last season, right?

Open DefinitionViewController.swift and add a new import below the existing UIKit import at the top:

import AlamofireImage

Then, add the following code below viewDidLoad:

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)
    
  if let imageURL = definition.imageURL {
    imageView.af_setImage(withURL: imageURL) { _ in
      self.activityIndicatorView.stopAnimating()
    }
  }
}

af_setImage is an extension of UIImageView that AlamofireImage provides. You call it to retrieve the image found in the definition imageURL. Once retrieved, the activity indicator’s animation stops.

Build and run, then perform your search again.

Downloading an image for a duck

Quack quack!