Modular iOS

Distributing universal iOS frameworks as XCFrameworks using Cocoapods

Anurag Ajwani
6 min readJul 13, 2020
Photo by Susan Holt Simpson on Unsplash

With the introduction of XCFrameworks we are now able to distribute universal frameworks as XCFrameworks to our integrators. XCFrameworks have the following advantage over universal frameworks:

  1. It’s fully supported by Apple
  2. It’s easier to build and maintain

With Xcode 12 we are now able to distribute and consume XCFrameworks using Swift Package Manager(SPM). However many iOS app projects are using Cocoapods to manage dependencies. Cocoapods too supports the distribution and consumption of XCFramework.

Why should you distribute your framework as XCFramework through Cocoapods? Here are the main reasons:

  • Apple might drop support of universal frameworks in the future
  • Can make it easier for for those transitioning from Cocoapods to SPM

In this tutorial I will show how to distribute a universal iOS framework as XCFramework using Cocoapods.

I assume you are familiar with iOS frameworks, xcframeworks, iOS development and Swift. I also assume you are familiar with the basics of dependency management, Cocoapods and Git version control.

I have used Swift 5.2.4 and Xcode 11.5 whilst writing this article.

How to distribute XCFrameworks using Cocoapods

In this section we’ll start by downloading a starter pack. The starter pack contains an iOS framework project and an iOS app. We’ll use the framework project to build an XCFramework which we’ll distribute through Cocoapods. In the next section we’ll use the iOS app to consume the XCFramework using Cocoapods.

The steps we’ll take:

  1. Download the starter pack
  2. Build an XCFramework
  3. Create git repo to store and version the XCFramework
  4. Install Cocoapods
  5. Create git repo for specification files
  6. Create specification file
  7. Publish specification file

1. Download the starter pack

Let’s start by downloading the starter pack. Open terminal and execute the following commands:

cd $HOME
curl https://github.com/anuragajwani/xcframework-cocoapods-tut/archive/starter.zip -o xcframework-cocoapods-tut.zip -L -s
unzip -q xcframework-cocoapods-tut.zip
rm -rf xcframework-cocoapods-tut.zip
mv xcframework-cocoapods-tut-starter xcframework-cocoapods-tut

We’ll only be using the terminal for this section of the post. Make sure to keep it open.

2. Build an XCFramework

Next we’ll an XCFramework from the iOS framework. I have included a build script within the iOS framework project to build the XCFramework. In this post I won’t cover the details of XCFramework.

In terminal execute the following commands:

cd ~/xcframework-cocoapods-tut/MyFramework
sh build_xcframework.sh

The script will produce an XCFramework named MyFramework.xcframework.

I won’t go into the details of the anatomy of an XCFramework and how to build it in this post. If you’d like to learn more check out my post on how to build universal frameworks as XCFrameworks.

3. Create git repo to store and version the XCFramework

Next we’ll create a git repository to store and track versions of the XCFramework. This will be the place where Cocoapods will fetch the XCFramework from. Execute the following commands:

cd ~/xcframework-cocoapods-tut
mkdir MyFrameworkDistribution.git
cd MyFrameworkDistribution.git
git init --bare
git clone ~/xcframework-cocoapods-tut/MyFrameworkDistribution.git ~/xcframework-cocoapods-tut/MyFrameworkDistribution
cd ~/xcframework-cocoapods-tut/MyFrameworkDistribution
touch README.md
git add README.md
git commit -m "Initial commit"
git push origin -u master

Next we’ll copy the generated .xcframework into the repository, commit and tag it. Execute the following commands:

cp -r ~/xcframework-cocoapods-tut/MyFramework/MyFramework.xcframework ~/xcframework-cocoapods-tut/MyFrameworkDistribution
git add MyFramework.xcframework
git commit -m "Added MyFramework.xcframework"
git tag -a 1.0.0 -m "Version 1.0.0"
git push origin master
git push origin --tags

Above we have tagged the commit containing MyFramework.xcframework with 1.0.0. This is how Cocoapods will find the right version when the integrator specifies that they want to install version 1.0.0 of our xcframework.

4. Install Cocoapods

Next let’s install Cocoapods to publish the XCFramework. To install Cocoapods run the following command:

gem install cocoapods

If you already have Cocoapods installed make sure you have version 1.9.0 or newer. You can check by running the following command:

pod --version

5. Create git repo for specification files

Next we’ll create a local git repository to hold Cocoapods specification files. We’ll also tell Cocoapods to register the specification repository in its list. Execute the following commands:

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

6. Create specification file

Next we’ll create a specification file for the framework. The specification must tell Cocoapods where to fetch the framework from and how to configure it. Execute the following command:

cat > ~/xcframework-cocoapods-tut/MyFramework.podspec <<-EOF
Pod::Spec.new do |s|
s.name = "MyFramework"
s.version = "1.0.0"
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/xcframework-cocoapods-tut/MyFrameworkDistribution.git", :tag => "#{s.version}" }
s.vendored_frameworks = "MyFramework.xcframework"
s.platform = :ios
s.swift_version = "4.2"
s.ios.deployment_target = '12.0'
end
EOF

7. Publish specification file

Finally we’ll publish the specification file to the soecification repository we created in step 5. Execute the following command:

pod repo push my-specs ~/xcframework-cocoapods-tut/MyFramework.podspec

That’s it. The XCFramework is now ready to be consumed using Cocoapods.

How to consume XCFrameworks using Cocoapods

In this section we’ll be installing and consuming the XCFramework from the previous section using Cocoapods. We’ll be making use of an already existing app that is included in the starter project.

The steps we’ll take:

  1. Create Podfile
  2. Install dependencies
  3. Consume XCFramework

1. Create Podfile

Before we can install any dependencies to the app we must create a file named Podfile. A Podfile holds information on the dependencies Cocoapods must install for a target in the Xcode project.

Let’s create the Podifle. Execute the following command:

cd ~/xcframework-cocoapods-tut/XCFrameworkCocoapodsDemo
cat > Podfile <<-EOF
target 'XCFrameworkCocoapodsDemo' do
use_frameworks!
pod 'MyFramework', '1.0.0', :source => "$HOME/xcframework-cocoapods-tut/MySpecs.git"
end
EOF

Above we have declared MyFramework as dependency of XCFrameworkCocoapodsDemo target. Also we specified that we’ll be using version 1.0.0 and where to find the specification file (:source).

2. Install dependencies

Next we have to tell Cococapods to install dependencies declared in the targets specified. Execute the following command:

pod install

3. Consume XCFramework

In this step we’ll be adding some code to the project. First let’s open the project in Xcode. Execute the following command:

open -a Xcode ~/xcframework-cocoapods-tut/XCFrameworkCocoapodsDemo/XCFrameworkCocoapodsDemo.xcworkspace

Next we’ll consume the functionality contained within MyFramework.xcframework. The functionality of the framework itself is not in scope in this tutorial.

Open ViewController.swift in XCFrameworkDemo. Then under import UIKitadd the following line:

import MyFramework

Next within the loginButtonTapped function add the following lines of code:

let loginViewController = LoginViewController()
self.present(loginViewController, animated: true, completion: nil)

And that’s it! Run XCFrameworkCocoapodsDemo app against simulators or devices and see it work.

Summary

In this post we learnt:

  • how to distribute XCFrameworks using Cocoapods
  • how to consume XCFrameworks using Cocoapods

Final Notes

If you’d like to learn more on how to distribute XCFrameworks through Swift Package Manager then you can learn more in my previous post:

Follow me on Twitter or Medium for more on iOS development!

--

--

Anurag Ajwani

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