Modular iOS

How to distribute iOS frameworks using Carthage

Learn how to distribute and consume iOS frameworks using Carthage

Anurag Ajwani

--

Great software also has a great installation experience. If the software is too hard to install few will use it.

Vector graphics sourced from https://www.vecteezy.com

When it comes to distributing iOS frameworks the installation process can vary depending on the form the framework is delivered. The two forms in which you can deliver your framework are:

  1. source code
  2. compiled framework

In this post we’ll focus on the first form. That means the source code is readable by the integrator.

The experience that Apple offers out of the box when installing a iOS framework leaves a lot to desire. First integrators need to get a copy of your code. Then they need to add it to their workspace. Finally they need embed the framework target into their app.

This isn’t a great experience. Once you have a new version of your framework the integrator will also have to update their copy of the source code.

So is there a better way to integrate with iOS frameworks? Yes there is! Carthage is an open source tool that help iOS app developers manage their dependencies.

In this post we’ll learn:

  • how Carthage works
  • how to distribute an iOS framework for Carthage
  • how to consume a framework distributed through Carthage

Each section is independently consumable. Feel free to skip any section as necessary.

I assume the reader is familiar with Git version control and has knowledge on iOS frameworks. I also assume the reader has basic knowledge of Swift, iOS development and iOS frameworks.

I have used Swift 5.2 and Xcode 11.4.1 for this post.

How does Carthage work?

Carthage makes use of git version control and, Xcode project files and schemes to discover which version of the code to use and how to build it.

For publishing frameworks that works with Carthag the tool is not required. However to make it work the Xcode project and git repository must be used correctly for Carthage to discover the framework and build it.

Below you can find a basic diagram of how Carthage works:

How Carthage works

How to distribute Swift frameworks using Carthage

In this section we’ll distribute an iOS framework through Carthage. The framework will be provided as creating a framework is out of scope for this tutorial.

The steps we’ll take:

  1. Retrieve the starter project
  2. Create local git repository
  3. Publish Swift framework version

1. Retrieve the starter project

Let’s retrieve the starter project. Open terminal and run the following commands:

cd $HOME
curl https://github.com/anuragajwani/distributing_ios_frameworks_carthage/archive/starter.zip -o ios_framework.zip -L -s
unzip -q ios_framework.zip
cd distributing_ios_frameworks_carthage-starter/MyFramework

The starter project contains a single iOS framework target with a single build scheme which is shared.

Note: the scheme must be shared for Carthage to be able to discover the framework.

The framework code contains a login screen which we’ll display to the user from within the integrator’s app.

2. Create local git repository

Next we’ll publish our framework so that Carthage is able to consume it. Carthage leverages Git version control to distribute and import frameworks.

Let’s create a git repository for our framework. Execute the following commands:

git init
git add --all
git commit --message "Added my framework project"

In this post we have created a local repository. We’ll be referring to this local repository as the one that will be reached by the integrator. However most likely you’ll need to use a git repository hosting service on the internet (i.e. GitHub) so your integrators can reach your code.

3. Publish Swift framework version

Carthage uses Git tags to determine which version of your code use. The tags must follow Semantic Versioning specification; this is how you decide whether to tag your version as 1.0.1 or 1.1.0. We won’t be covering Semantic Versioning specification in this post. We’ll only publish a single version of our iOS framework; 1.0.0. Execute the following command:

git tag -a 1.0.0 -m "Version 1.0.0"

And that’s it! You are now able to import the framework using Carthage. Let’s see how to integrate with it in the next section.

How to consume iOS frameworks using Carthage

In this section we’ll be consuming an iOS framework framework from the previous section. We’ll create an app from scratch.

The steps we’ll take:

  1. Create a new iOS app
  2. Install Carthage
  3. Create a Cartfile
  4. Fetch dependencies
  5. Link dependencies
  6. Consuming the framework

1. Create a new iOS app

Let’s create an app that will consume our Swift package. Open Xcode and from menu select File > New > Project…

When prompted “Choose a template for new project:” select Single View App and then click Next.

On new project options name the product CarthageDemo. Select the language as Swift. Select Storyboard as the “User Interface” option. Keep all checkboxes unchecked. Click Next.

When prompted where to save it use the keys ⌘ + ⇧ + G (Command + Shift + G). When prompted “Go to the folder” enter ~/distributing_ios_frameworks_carthage-starter/ then click Go.

Finally click Create.

2. Install Carthage

Before we can install our iOS framework using Carthage we need to first install Carthage itself. Open terminal and execute the following commands:

cd $HOME
curl -L https://github.com/Carthage/Carthage/releases/download/0.34.0/Carthage.pkg -o Carthage.pkg
sudo installer -pkg Carthage.pkg -target /usr/local

You’ll need to enter your macOS user password to install Carthage in /usr/local.

3. Create a Cartfile

Next we’ll specify Carthage what dependencies our framework requires. The way to this is by creating a file named Cartfile which will contain a list of dependencies for our project. Execute the following commands:

cd ~/distributing_ios_frameworks_carthage-starter/CarthageDemo
touch Cartfile

Next declare the MyFramework dependency. Execute the following command:

cat > Cartfile <<-EOF
git "file://$HOME/distributing_ios_frameworks_carthage-starter/MyFramework" "1.0.0"
EOF

In case of using a remote git repository just change the url between double quotes with the desired git url.

4. Fetch dependencies

Next let’s tell Carthage to fetch our dependencies. Execute the following command:

carthage update
terminal output for “carthage update”

Notice that Carthage fetches and builds the dependencies. The build is a universal framework that works with devices and simulators. Xcode doesn’t offer this out of the box. However Carthage doesn’t link the built framework with our project. We’ll do that in the next step.

5. Link dependencies

Next we’ll need to link MyFramework to CarthageDemo. We have to do that in the CarthageDemo project settings.

First let’s open project settings. Open project navigator (from menu select View > Navigators > Show Project Navigator).

Next select CarthageDemo project file (at the top level with the blue icon).

Then select the CarthageDemo app target.

Next select the General tab and within the main pane scroll down to “Frameworks, Libraries and Embedded Content”.

Here we’ll drag and drop the framework. We need to locate the framework and drag and drop it in “Frameworks, Libraries and Embedded Content”.

To open the location of the framework execute the following command in terminal:

open -a Finder ~/distributing_ios_frameworks_carthage-starter/CarthageDemo/Carthage/Build/iOS/

Next drag and drop MyFramework.framework into “Frameworks, Libraries, and Embedded Content

And that’s it the framework is now linked to the app.

6. Consuming the framework

In the final step we’ll consume the frameworks functionality. We’ll be presenting the login screen within the framework when the user taps on the login button.

First let’s import the framework in our ViewController. Open ViewController.swift and add the following line under import UIKit:

import MyFramework

Next let’s add functions to create the login button and when tapped present the login screen:

private func addButton() {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Login", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(self.login), for: .touchUpInside)
self.view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
])
}
@objc private func login() {
let loginVC = LoginViewController()
self.present(loginVC, animated: true, completion: nil)
}

Finally let’s add the button by calling the addButton function. Add the following line at the end of viewDidLoad:

self.addButton()

That’s it! Run the app and see it in action 🎉.

Summary

In this post we learnt’t:

  • how Carthage works
  • how to publish a framework for Carthage
  • how to consume a framework using Carthage

Final notes

You can find the full source code below:

Publishing and consuming frameworks using Carthage is very simple. However Carthage is not the only dependency manager for iOS. The most popular dependency management tool for iOS is Cocoapods. Additionally Swift Package Manager is the latest dependency management tool to join the group. Swift Package Manager is bundled with Swift. However Swift Package Manager does not support framework distribution only libraries for now.

A full comparison of the advantages and disadvantages of each tool is not in scope for this post. Later I’ll publish a post comparing the most popular tools.

Stay tuned for more on iOS development! Follow me on Twitter or Medium.

--

--

Anurag Ajwani

Senior iOS Engineer at Travelperk. 7+ years experience with iOS and Swift. Blogging my knowledge and experience.