iOS: Umbrella Framework with CocoaPods

This article will teach you how to distribute an iOS private vendored umbrella framework with CocoaPods.

Table of Contents

1. Intro
2. Create a Framework with dependencies
3. Create a Pod Umbrella Framework
4. Distribute the Pod
5. Integrate the Umbrella Pod

Intro

In one of my previous projects I had the particular need to create a framework incorporating two more frameworks. The goal was to hide as much as possible the use of those two sub-frameworks.
Everything then had to be published as a private and closed Pod.

A framework is no more than a set of methods and logics that can be reused. A framework is closed, so it is not possible to read the source code, just the methods and classes that are defined as open or public are visible and can be recalled.

In the Apple development environment the incorporation of one framework into another is called “Umbrella”. This type of framework is meant to hide the use of some sub-frameworks and thus facilitate their use.

Before proceeding into this adventure it is good to know that Apple does not officially support the Umbrella Frameworks for iOS, indeed, citing the official documentation:

Don’t Create Umbrella Frameworks.

While it is possible to create umbrella frameworks using Xcode, doing so is unnecessary for most developers and is not recommended. Apple uses umbrella frameworks to mask some of the interdependencies between libraries in the operating system. In nearly all cases, you should be able to include your code in a single, standard framework bundle. Alternatively, if your code was sufficiently modular, you could create multiple frameworks, but in that case, the dependencies between modules would be minimal or nonexistent and should not warrant the creation of an umbrella for them.

 https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/CreationGuidelines.html “Apple Documentation”

Following this guide you will create a sub-framework with some dependencies of CocoaPods, an Umbrella framework that incorporates the sub-framework with further dependencies of CocoaPods.
Then you will publish everything as a private and closed pod, finally, we will integrate everything into a test project.

Create a Framework with dependencies

As a first step we create the sub-framework with his dependencies. In my case this was a cocoapod vendored framework, so I didn’t have to create it.

Let’s start by creating a new Cocoa Touch Framework:

Cocoa Touch Framework

The next step is to create a Cross-platform Aggregate Target:

An aggregate target has no associated product and no build rules.

Aggregate Target

You can define the Podfile with the dependencies needed to build the Sub-framework, and then you can do the:

pod install

For this example the Podfile is the following:

Subframework Podfile

It is important to remember that the minimum version of iOS must be the same between the various podfiles, the subframework, the umbrella framework and the example project, otherwise it is almost certain to run into compilation errors.

In this case I set to 10 the minimum version of the Podfile, then I verify in my project that:

IPHONEOS_DEPLOYMENT_TARGET = 10.0

You continue by opening the newly created workspace after the `pod install`, and you create an example class that can be recalled from the future Umbrella Framework. Remember that only the open and public classes and methods will be visible.

Below is the example class for this guide, which makes a simple request using the Alamofire dependency and executes a completion function passed as parameter.

Subframework example class

The Target Membership for the files does not need to be set also on the Target Aggregate.

At this point it is necessary to add a run script that does the job of exporting the framework at each build of the aggregate target for you.
Add a script phase like this:

New run Script Phase

Add the following code:

Archiving srcipt

At the beginning of the script, in the OUTPUT_FOLDER variable it is possible to specify another path as destination of the framework.

Go to Build Settings from Aggregate Target and add `-fembed-bitcode` to Other C Flags. If you don’t give this flag, you might not be able to archive the app with this framework.

Now select Generic iOS Device as target build, select the Aggregate Target and make your build. If all goes well, in the root of the project or in the directory you specified you will find the framework freshly generated.

Congratulations, you have just created your first framework! 😎

Create a Pod Umbrella Framework

It’s time to create the Umbrella Framework, which is the external layer that will contain the sub-framework. This is the framework that will be distributed via CocoaPods and delivered to the customer.

First create a new Xcode project and repeat the exact same steps of the SubFramework of the previous section:

1. Create a Cocoa Touch Framework (in this tutorial I call it Umbrella);
2. Create an Aggregate Target;
3. Fill your Podfile with all the dependencies of the subframework plus the ones you will need in the Umbrella Framework. Remember the iOS version;
4. Run a `Pod Install`
5. Add a phase script for compilation to the target aggregate as in the previous section. Don’t forget the flag -`fembed-bitcode`.

In the script that you have just copied, you should modify the output directory in this way

from:

OUTPUT_FOLDER=${PROJECT_DIR}

to:

OUTPUT_FOLDER=${PROJECT_DIR}/Umbrella`

The new project has created a sub-directory called “Umbrella” or whatever is the name of your project with a .plist and the Umbrella.h files. In this folder you need to add a LICENSE.txt file that will contain your license, and the Subframework.framework compiled in the previous section.

In the root of the project a folder called “Sources” is created, in which we will put all the Umbrella Framework source code.

Now create two different Umbrella.podspec that you will fill up later: one you will use to distribute the compiled vendored framework; the other one to distribute the not compiled version, useful in the test environment and for making any changes directly from the integration project.
Place the first .podspec of the root and the second in the Umbrella folder.

Eventually you’ll find yourself with a structure like this:

Umbrella folder structure (part 1)
Umbrella folder structure (part 2)

This structure will allow you to keep the sources divided by the project you need for archiving the framework. It will be very useful when you create the two podspecs, one for the publication of a vendored framework and the other for the publication of an open framework, excellent for testing.

Now has come the time to create the right connections in the project and make everything work properly.

Add the Sources folder and the Subframework.framework to the project.

Then navigate to the Build Phase Settings of the target Umbrella and add a New Copy Files Phases.

New Copy Files Phase

Make sure the Subframework.framework appears in the following fields:

  • Link Binary With Libraries
  • Copy Bundle Resources
  • Copy Files, with destination set to Frameworks and the active code sign.
Umbrella Build Phase

Finally, always in the Build Phases of the Umbrella target, verify that your header file is correctly set to public:

Umbrella public header

Create a new Swift file in the Classes folder, where you will write your example function:

Umbrella Example Class

This function will recall the example function of the Sub-framework and upon completion will print “hello from Umbrella”.

You are now ready to make the final archive of your project.
Select the aggregate target, as destination set a physical device or the generic device and start a build. If all goes well, in the Umbrella directory specified in the script of your project, you can find the new Umbrella Framework which includes the Sub-framework.

Congratulations! 🎊

You can already use your new framework, but if you want to make it a Pod, continue with the next section.

Distribute the Pod

Before changing the pospecs to prepare the release, you need to upload your framework to some host.
In my project I used firbase storage, but there are also other valid options like bitbucket, gitlab and github.

What you will need to host will be a zip containing the Umbrella.framework and the LICENSE.TXT file.
Name the zip with a name different from the framework, like instead of just Umbrella.zip, you can call it UmbrellaFramework.zip.

Load the zip into your host by maintaining a structure of a folder that indicates the framework version, with a zip inside containing the framework and the license.

Like this:

/1.0.0/UmbrellaFramework.zip

The time has come to fill the two podspec files.

The first podspec you must create is the one for the development environment.
Then it will refer to the git url in which you created your umbrella framework.
Open the podspec file located in the root of the project, and copy in it:

Umbrella Test Podspec

Update the podspec with your project information.

Instead, the podspec that will be used for the real distribution is the one located in the Umbrella subfolder.

Open it and copy this inside of it:

Umbrella Distribution Podspec

Also in this case you need to update the podspec with your project information, but instead of putting the git address of your project, put the address where your compiled and zipped framework will be hosted.

For each version, remember to update your podspec correctly, and to upload the new zip to the correct folder.

You are almost done, now you need to create a repo git where to publish your Podspec for anyone who wants to be able to install your framework.

I used GitHub with a private repo, but even in this case you can use any git tool you prefer.

I called it UmbrellaSDK, you can choose any name you like. Once created, you need to make some steps in the terminal:

pod repo add [REPO_NAME] [SOURCE_URL]
pod repo push [REPO_NAME] [POD_NAME].podspec — allow-warnings — skip-tests

Where:

  • REPO_NAME: is the name you want to use to identify your repo, I used Umbrella
  • SOURCE_URL: is the url of the repo you have just created
  • POD_NAME: is the name of the pod, which you used for your podspec, in this case Umbrella.
  • - -allow-warnings: before pushing the podspec, CocoaPods executes a lint to verify that everything is correct, with this flag you’re telling it to allow warnings, I put it to speed up the test.
  • - -skip-tests: also in this case I put the flag to speed up the test, in the production environment it is better to remove it and let CocoaPods run its tests.

If your push repo pod has been successful then you have finished your publication and you are ready to integrate everything into a test project.

And once again, congratulations! 🎊

Integrate the Umbrella Pod

Create a new project from Xcode where you will test the newly published framework.

To install the Umbrella framework it’s required to make some changes to the Podfile of your project.

If you still do not have initialized CocoaPod on you project open the terminal and in the root of your project type:

pod init

At this point you have a Podfile in your project. Open it:

open Podfile

The podfile is already provided with a starting template, override the template with the below lines of code:

Umbrella Integration Podfile

It’s important to replace the correct source of the Umbrella with the git repository you have created in the previous section.
In second place, remember to rename ‘Your_Target’ with the target of your Application defined in the project schemas.
Save the Podfile and close it.

In the code comments you’ll find the proper code if you want to use the local pod, instead of using the vendored framework published online. Write the correct path to the podspec in the root of the Umbrella project.

One thing is still missing: you need to add the signing script that will propagate your codesign to all the Umbrella framework and sub-frameworks.

In the terminal type:

touch sign.sh
chmod 777 sign.sh
open sign.sh

And in the script file just opened copy these lines:

Signing script

Replace the name of the framework if is different. Save the script and close it.

At this point you can install the Umbrella framework. In the terminal type:

pod install

When the installation is completed, open your new Workspace, and you are ready to work with the Umbrella framework.
You just need to add in your files:

import Umbrella

Now you can enjoy you brand new Private Vendored Pod Umbrella Framework😎.