Making a Cocoapod in Swift with an Objective-C .framework dependency

Building a cocoapod framework in swift is fairly easy, and there are plenty of tutorials and materials on how one can do it. However, making one that uses a .framework dependency, written in ObjC, can be quite a challenge. A notable example of this is the official framework of the popular Spotify app.

During the development of a fitness tracking application, our client requested that a Spotify mini player display inside the app. To make this happen, we decided to make a Swift framework that deals with the Spotify SDK dependency. This would provide helper classes to minimize the amount of code written in the app itself, along with a reusable Spotify component. This task may not seem very difficult at first sight, but looks can be deceiving.

In this article, we take a look at why this wasn’t so straightforward, and how we developed a solution to this intriguing issue.

Swift and Objective-C interoperability

Typically, importing Objective-C files into your Swift application code relies on bridging headers. These are header files that can import every Objective-C header file wanted to be exposed to Swift. After doing so, one should be good-to-go and can freely use those files.

However, making the framework is not so simple, as bridging headers cannot be used in this case. Instead, an umbrella header is required.

Umbrella headers and module maps

Most frameworks have an umbrella header. An umbrella header is simply a header file that groups public headers, and by default, has the same name as the framework itself.

For example, the SpotifyiOS.framework SDK has the umbrella header file “SpotifyiOS.h”, with the following content:

As we can see, this particular umbrella header file includes two other headers. Those two also include additional headers, and so on.

However, to use a framework as a Cocoapods dependency, an umbrella header file is not enough.

Objective-C frameworks need to define a module to be used by Swift, and that module is defined by the modulemap file. This file is used by Clang, which is described in-detail here: https://clang.llvm.org/docs/Modules.html#module-map-language.

Most frameworks provide a modulemap file, but not all of them. In fact, the Spotify SDK does not, and this prevents making a Cocoapod written in Swift that has a dependency on it. Fortunately, there is a clever workaround to this ( described below).

Solution

The solution to this problem is to make a modulemap file for the framework. As an example, here are the steps needed to apply this to the Spotify SDK.

The key to making this work is the prepare_command attribute in the “Podspec” file of the Cocoapods framework. We can set a path to an executable bash script as a value, so that it runs after the pod is downloaded.

More info on this command.

First, go to the root of the framework project in Terminal and issue the following commands:

  • touch prepareSpotify.sh
  • chmod 777 prepareSpotify.sh
  • open prepareSpotify.sh

Now we can write the script that will make the modulemap file for the Spotify SDK. Copy the code into the prepareSpotify.sh file, then save it:

#!/bin/sh
if
[ “$#” -eq 1 ]; then
BASE_DIR=$1
else
BASE_DIR=$(exec pwd)
fi
rm -fR ios-sdk
mkdir ios-sdk
curl -OL https://github.com/spotify/ios-sdk/archive/v1.2.2.zip
unzip -o v1.2.2.zip
mv ios-sdk-1.2.2/SpotifyiOS.framework ios-sdk
rm v1.2.2.zip
rm -fR ios-sdk-1.2.2
BASE_DIR="${BASE_DIR}/ios-sdk"
echo "BASE_DIR: ${BASE_DIR}"
MODULE_DIR="${BASE_DIR}/SpotifyiOS.framework"
echo "MODULE_DIR: ${MODULE_DIR}"
mkdir -p "${MODULE_DIR}"
printf "module SpotifyiOS {\n\
header \"Headers/SpotifyiOS.h\"\n\
export *\n\
}" > "${MODULE_DIR}/module.map"
echo "Created module map.”

This script downloads the Spotify SDK, and makes a modulemap file for it. The result is the following folder structure, which can be seen when issuing the “pod lib lint” command:

The modulemap file module.map was created with the following contents:

It defines the module SpotifyiOS, so that we can now import our code, and names the umbrella header file situated under the Headers symbolic link.

Finally, import SpotifyiOS (or the chosen framework used) to the pod’s source code and use it as needed.

Once the framework is ready to be released, there’s nothing different to be done than with a simple pod. When the client application developer installs the pod, the script will be executed to seamlessly add the modulemap to the SDK, making it able for use right out-of-the-box.

Summary

In this case, a simple task turned out to be an unexpectedly challenging endeavor. While Apple made it easy to use Objective-C code in the Swift codebase (and vice-versa), there are still difficult cases that emerge — especially with frameworks. Understanding frameworks and modules are the key to solving these problems.

Here are two excellent articles on this topic that can shed more light on frameworks and Objective-C and Swift interoperability:

--

--

--

Design, develop, deliver razor sharp digital products

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Anoma Research and Development Update: February 2022

On using Github Issues as your project management tool

Simplifying the FragmentManager API: Multiple Fragment backstacks on Android

Docker volumes changes in compose version 3

How I monitor my Raspberry Pi trackers

How I monitor my Raspberry Pi trackers

AMA with BERRY | Transcript

Studi Kasus Agrikultur Farmers’ Cooperative

Why Software Development Slows Down When You Try to Speed It Up

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
BitRaptors

BitRaptors

Design, develop, deliver razor sharp digital products

More from Medium

Bubble Sort in Swift

Regular Expression aka RegEx

Preventing merge conflicts with XcodeGen for your xcode project using xcodeGen and project.yml file

UISwitch with Thumb Image