Distributing Swift Frameworks

Distributing Swift Frameworks via Cocoapods

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

In my previous post we learnt how to reuse and share code using Swift frameworks. We created a project with a framework target that contained a login screen. Then we added the framework and the app projects in an Xcode workspace. Finally we linked and consumed the framework to the app through the workspace.

We had the responsibility to fetch and and link the framework project. We didn’t even have a way to manage different versions of the framework.

So the framework installation and management experience is not great. Is there a simpler way to manage and install frameworks? 🤔

Yes there is! 🎉 Cocoapods is a popular tool that simplifies installing and sharing Swift frameworks.

In this post we will walk through the process of sharing a Swift framework through Cocoapods. Later we will consume it inside an app.

We will host our framework in a git repository which Cocoapods will access. In this article I assume you are familiar with git.

Creating a framework is beyond the scope of this article too. To see how to create a framework checkout my previous post. A framework project will be provided.

Finally we will be only be talking what Cocoapods can do for Swift frameworks.

Before we get started let’s have a quick overview of how Cocoapods work; it will help us to work with it.

This guide uses Xcode 10.1 and Swift 4.2.

How does Cocoapods work?

In this section we will be looking at a simple overview of how Cocoapods work from two different perspective.

  1. Installing frameworks to an iOS app project
  2. Publishing frameworks to Cocoapods.

Feel free to skip any or all of the section if you are already familiar with how Cocoapods work.

1. Installing frameworks to an iOS app project

Let’s start at looking at the simplified overview of how Cocoapods works when we ask the tool to install a Swift framework named FrameworkA to our iOS app project called MyApp.

We tell Cocoapods that we want to install FrameworkA in MyApp target in a file named Podfile at the root level of the project directory.

Following our example, the Podfile should look similar to the following:

target 'MyApp' do
pod 'FrameworkA'
end

In the example above we are telling Cocoapods that our project has a target named MyApp. We are also telling Cocoapods that the MyApp target requires a framework named FrameworkA that is being shared through Cocoapods. A shared framework in Cocoapods is refered to as a pod.

Once we have captured our project targets and its requirements we have to tell Cocoapods to set up our project based on our project specification. For that we have to run pod install.

During a pod install Cocoapods will read our Podfile and carry out any necessary actions to configure our project accordingly.

Here is a quick diagram of whats going on during a pod install:

Cocoapods — pod install overview

When Cocoapods reads our Podfile it first fetches the specification of FrameworkA. A place that holds specifications is called a specification repository or store. By default it searches only the Cocoapods specification repository. However we can point to other specification repositories, even our own.

Note we didn’t specify a version of the FrameworkA to install. In that case Cocoapods will fetch the latest version available.

At the end of the pod install execution, Cocoapods will have generated a workspace for us with the framework already linked to our app target! 🎉

In very few steps we were able to achieve installation of a framework what would take many more steps using only the native Apple provided tools.

2. Publishing frameworks to Cocoapods

Now that we understand how Cocoapods installs frameworks, the only thing we have to do in order for other apps to use our framework is to publish the specification of our framework to the specification repository.

Cocoapods publish

Let’s get a feel for it in action!

Getting started

In this guide we will:

  1. Install Cocoapods
  2. Retrieve an Xcode project containing a framework target
  3. Create a local git repository to host our framework project
  4. Create a local specification repository
  5. Publish our framework specification to our specification repository
  6. Install our framework into an app

Before diving in first let’s open the terminal app. We will be using terminal throughout the guide to accomplish sharing and installation of our framework.

1. Installing Cocoapods

To start off with let’s install Cocoapods, the tool that will ease sharing and installing frameworks. In terminal type or, copy & paste the following command.

gem install cocoapods
Installing Cocoapods

2. Retrieve starter project

Next, let’s setup the starter framework project which we will share via Cocoapods. Lets download and place the starter project in our home directory via terminal.

cd ~
curl -o MyFramework.zip -L https://www.dropbox.com/s/5vykpag4xb5vh51/MyFramework.zip -s
unzip -q MyFramework.zip

3. Create locally hosted git repository

For this article we’ll be hosting a git repository for the MyFramework project locally in our Macs. To create a locally hosted git repository execute the following commands:

mkdir ~/MyFramework.git
cd ~/MyFramework.git
git init --bare
Locally hosting a git repository for MyFramework project

Above we have created a directory that will hold our git repository (with extension .git) and initialised a git repository inside that directory.

Now that we have a place where to host our framework lets add the MyFramework project content to that place. Execute the following commands:

cd ~/MyFramework
git init
git remote add origin ~/MyFramework.git
git add .
git commit -m "Initial commit"
git tag -a 0.0.1 -m "Version 0.0.1"
git push origin -u master
git push origin --tags
Storing MyFramework in git repository

We now have our source code stored in a local git repository.

4. Create local specification repository

Let’s create a repository to hold specifications. This is the place where we publish our framework specification. To do so execute the following commands:

mkdir ~/MySpecs.git
cd ~/MySpecs.git
git init --bare
git clone ~/MySpecs.git ~/MySpecs
cd ~/MySpecs
touch README.md
git add README.md
git commit -m "Initial commit"
git push origin -u master
pod repo add my-specs ~/MySpecs.git
Create specification repository

The first few commands are similar to the previous section where we created a git repository to hold our framework. The last command pod repo add my-specs ~/MySpecs.git tells Cocoapods to add ~/MySpecs.git to the list of specification repositories and giving it the name of my-specs. You can check the list of specification repositories held by your Cocoapods by running pod repo list.

List of specification repositories — pod repo list

5. Publish specification for MyFramework

Before pushing a Cocoapods specification let’s add a git tag to our project so Cocoapods can refer to specific snapshot of the project. A git tag will add an identifier to our current state of the project. As git is not the scope of this guide simply know that addition to our project framework won’t affect the code delivered via Cocoapods as Cocoapods will refer to the current state of the project.

Let’s tag the current state of our framework as version 0.0.1. Run the following commands:

cd ~/MyFramework
git tag -a 0.0.1 -m "Version 0.0.1"

Next, let’s add a Cocoapods specification to the project. Run the commands below:

cat > ~/MyFramework.podspec <<-EOF
Pod::Spec.new do |s|
s.name = "MyFramework"
s.version = "0.0.1"
s.summary = "A brief description of MyFramework project."
s.description = <<-DESC
An extended description of MyFramework project.
DESC
s.homepage = "http://your.homepage/here"
s.license = { :type => 'Copyright', :text => <<-LICENSE
Copyright 2018
Permission is granted to...
LICENSE
}
s.author = { "$(git config user.name)" => "$(git config user.email)" }
s.source = { :git => "$HOME/MyFramework.git", :tag => "#{s.version}" }
s.source_files = "MyFramework/**/*.swift"
s.resources = "MyFramework/**/*.xib"
s.platform = :ios
s.swift_version = "4.2"
s.ios.deployment_target = '12.0'
end
EOF

The above command will create a file called MyFramework.podspec and add the contents between EOF into the file.

Here we are adding the necessary information for Cocoapods to be able to fetch and install the correct code into the installers project. You can find other Cocoapods specification attributes to allow you to configure your framework according to your project needs here.

We have now created a specification. The last thing to do is to make this specification available to the framework consumers by pushing this specification to a specification repository.

Cocoapods publish

Run the following command to push MyFramework specification to the private specification repository:

pod repo push my-specs ~/MyFramework.podspec
pod repo push

Great job! Our framework is now listed in our specification repository and is ready to be consumed by apps! In the final step we will consume MyFramework via an app.

6. Install MyFramework in an app

Before we can test the installation of MyFramework via Cocoapods we need an app in which to install it to. Let’s fetch MyApp project from my previous post.

Run the following commands:

cd ~
curl -o MyApp.zip -L https://www.dropbox.com/s/jfdrj58lgrhjc4t/MyApp.zip? -s
unzip -q MyApp.zip

Next let’s install MyFramework in MyApp. We will first have to create a Podfile where we will specify MyFramework to be installed in MyApp. Run the following commands:

cd ~/MyApp
cat > Podfile <<-EOF
target 'MyApp' do
use_frameworks!
pod 'MyFramework', '0.0.1', :source => "$HOME/MySpecs.git"
end
EOF

Note use_frameworks! is required when using frameworks.

Next let’s tell Cocoapods to install the specified frameworks in the Podfile. Run pod install command to do so.

pod install

Open MyApp.xcworkspace in Xcode and run the app. And that all! 🥳

Final notes

Before we end this post, note MyFramework source code is copied into a Pods project and wrapped in a framework target. Every time we build the app MyFramework is also built and then attached to the app.

Installed MyFramework in MyApp workspace
Targets in Pods project
MyFramework building in MyApp build process

Summary

In this guide we have learnt

  • how cocoapods work
  • how to distribute a framework through Cocoapods
  • how to install our distributed framework via Cocoapods

We have seen how simple it is from an integrators point of view to install and manage frameworks using Cocoapods. Cocoapods can attract more developers to install and consume your framework thanks to its simplicity.

Next steps

In this guide we saw that Cocoapods shares source code as well as assets with the integrator and then packages them in a framework on the integrators end. We saw the integrator then compiles that code as part of their app build process. But what if we don’t want to share the code? Or maybe we would like to save time from compiling large chunks of code from our apps that are shared and that rarely changes. Is there a way to shared compiled frameworks via Cocoapods?

You will find the answer to that question in the next article. Follow me on Twitter or Medium and stay tuned!