Creating a private iOS framework in Swift and Distributing it using CocoaPods

Sundaresh Joshi
8 min readSep 17, 2018

--

Frameworks are a collection of code and resources to encapsulate functionality that is valuable across multiple projects.

A swift framework includes both a Swift module file, which communicates source-level information of the framework’s API, and a shared library, which provides the compiled implementation that is loaded at runtime.

There are a different ways of distributing your iOS framework. It can be distributed by pushing it to any git repository manager (like github, gitlab etc). Here you can push the entire framework project to the remote repo and can make your source code visible or you can just push your iOS framework without sharing the source code.

In this article we are going to learn how to create a private framework in swift and to distribute it using CocoaPods without sharing its source code.

Let’s start with creating a framework.

Creating a Swift Framework

Here we will create a HelloWorld framework which contains a Logger class which in turn has a class func printHelloWorld, this prints “Hello World!” text.

Open Xcode

From Top menu select Cocoa Touch Framework and click Next

Set product name to HelloWorld and choose the language as Swift.

Click Next and Create.

If you expand the HelloWorld folder in the project, you will find two files: Info.plist and HelloWorld.h — the latter is the header file that will be used to communicate between your framework and the host app.

Click on FileNewFile

Select Cocoa Touch Class and click Next

Set the class name as Logger and make sure it is a subclass of NSObject.

Click Next and Create.

Now Logger class will look like this

class Logger: NSObject {}

Replace Logger class with the following lines of code.

@objc public class Logger: NSObject {    @objc public class func printHelloWorld() {        print("Hello World!")    }
}

Logger is a public class which contains printHelloWorld func, which prints “Hello World!” text.

public specifier is added so that host app can access both Logger class and printHelloWorld func.

@objc → This tells the compiler that this piece of Swift code can be accessed from Objective-C, which is useful if the host app is written in Objective-C.

Generate Universal Framework

To make a universal framework which can be used on both device and simulator, you can configure a post-archive script that will build your framework for the iOS simulator after you archive it, and merge both the simulator and iOS binaries into one fat universal framework.

Follow the below steps to generate a universal framework.

  • Go to Build Settings - > Architectures -> Set Build Active Architecture Only to NO.
  • Build your framework in iPhone 5 and iphone 6 or above simulators which ensures that your framework supports both i386 & x86_64 architectures.
  • To have Xcode run the script after archiving automatically, select the HelloWorld target, click Product -> Scheme -> Edit Scheme (or Cmd + Shift + <), and configure a Run Script post-action for the Archive command:
  • Copy the below run script and paste into the Run script window.
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal# Make sure the output directory existsmkdir -p "${UNIVERSAL_OUTPUTFOLDER}"# Next, work out if we're in SIM or DEVICExcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean buildxcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build# Step 2. Copy the framework structure (from iphoneos build) to the universal foldercp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directoryBUILD_PRODUCTS="${SYMROOT}/../../../../Products"cp -R "${BUILD_PRODUCTS}/Debug-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directorylipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_PRODUCTS}/Debug-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"# Step 5. Convenience step to copy the framework to the project's directorycp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"# Step 6. Convenience step to open the project's directory in Finderopen "${PROJECT_DIR}"fi
  • Be sure to select HelloWorld for the Provide build settings from setting and click Close to apply the changes.
  • And Finally, archive the framework by clicking Product --> Archive in the menu bar. If the option is greyed out, make sure to select a physical iOS device and not the iOS simulator.
  • A finder should open up to your project directory with a universal HelloWorld.framework inside it.

Right Click on it and Select Show Original where you will find the required Universal framework, copy and paste it in some folder.

Now that you have the Universal Framework, you can use it in your host app.

Testing in a host app

  • Open your host app where you want to integrate the HelloWorld framework or create a new xcode project in case you don’t have any existing ones.
  • Go to your Project(host app) Inspector’s General tab and scroll down to where it says, Embedded Binaries. Click the + button and then Add Other. A Finder window will drop down, and here you need to select the HelloWorld.framework.

Note: If the host app is written in Objective C, go into Build Settings → At the top select All and Combined → Under Build Options , set Always Embed Swift Standard Libraries to Yes.

Import Framework

import HelloWorld  // Swift@import HelloWorld; // Objective C

Add import statement at the top of your viewController file (viewController.m file in case the host app is written in Objective C).

You should be able to access both the Logger class and printHelloWorld func of the framework.

 Logger.printHelloWorld().

You have successfully created and integrated your Swift Universal Framework! ✌️

Distributing the Swift Framework using Cocoa pods.

Now that you have created the framework, you want it to be private and want to distribute it to other developers within your organisation.

Creating a private repository

  • Create an account in https://gitlab.com if you don’t have one already.
  • Create a new private repo and name it as HelloWorld
  • Add MIT License to your project.

Note: You can also create a private repo in any other web-based Git-repository manager like github, bitbucket etc.

Install CocoaPods

Obviously, to create a pod, you need to install CocoaPods.

Follow CocoaPods Installation Guide to install the CocoaPods.

Creating a Pod Spec File

Create a new folder and name it as Remote in your desktop(or anywhere you want).

Open the Terminal & use cd to switch to Remote folder.

Run the following command:

git clone https://gitlab.com/YOUR_USERNAME/HelloWorld.git (url pointing to your git repository)

This will clone HelloWorld repo, use cd to switch to HelloWorld repo which is inside Remote folder.

Now Copy-Paste your binary, HelloWorld.framework to HelloWorld repo.

Run the following command:

pod spec create HelloWorld

This creates the file HelloWorld.podspec in the current folder. It’s a template that describes the pod and how to build it. Open it in text editor.

Replace the contents of podspec file with the following.

#
# Be sure to run `pod spec lint HelloWorld.podspec' to ensure this is a
# valid spec and to remove all comments including this before submitting the spec.
#
# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#
Pod::Spec.new do |s|# ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# These will help people to find your library, and whilst it
# can feel like a chore to fill in it's definitely to your advantage. The
# summary should be tweet-length, and the description more in depth.
#
s.name = "HelloWorld"#s.version = "INITIAL_VERSION OR TAG"
s.version = "1.0.0"
s.summary = "HelloWorld is a framework"s.homepage = "https://gitlab.com/YOUR_USER_NAME"
s.description = "HelloWorld is a swift framework which has a logger class and printHelloWorld func"
s.license = "MIT"s.author = { "YOUR_NAME" => "YOUR_MAIL_ID" }
s.platform = :ios, "10.0"
s.ios.vendored_frameworks = 'HelloWorld.framework'
#s.swift_version = "Swift version of the framework"
s.swift_version = "4.1"s.source = { :git => "https://gitlab.com/YOUR_USERNAME/HelloWorld.git", :tag => "#{s.version}" }s.exclude_files = "Classes/Exclude"end

Now go back to Termial and Run the following command

pod lib lint

This validates your library and podspec configuration locally, if everything is correct in podspec file then you should get the following message.

HelloWorld passed validation.

All Right!

Now you need to push the HelloWorld binary framework and podspec file to the gitlab repo.

Run the following commands one-by-one.

// add HelloWorld.framework and HelloWorld.podspec
git add -A
// commit the changes with a commit msg
git commit -m "Add framework and podspec file"
// push the changes to the remote
git push -u origin
// create a tag, the version should match with the one that you have mentioned in the podspec file
git tag 1.0.0
// push the tag to the remote
git push --tags

Next, Run the following command in the terminal to test your podspec validation

pod spec lint

you will get a success msg if all the configurations in the podspec file are correct.

HelloWorld.podspec passed validation.

Awesome!👌 You’re just a step away from testing your framework in a host app through CocoaPods!

Note: Give access to your Gitlab repo to all the members with whom you want to share your framework.

Testing using CocoaPods

Create a brand new xcode project and name it as Sample.

Open the Terminal & use cd to switch to your new project directory.

Initialise pod by running the following command

pod init

This will create pod file.

Add the following line below use_frameworks! in your pod file.

pod 'HelloWorld', :git => "https://gitlab.com/YOUR_USERNAME/HelloWorld.git" // url pointing to your HelloWorld repo

Save it and then install the pod by running the following command.

pod install

This will pull the HelloWorld framework from the Gitlab repository.

Finally, it creates a Sample.xcworkspace file.

Close the HelloWorld and new Sample app projects if they are open, and then open Sample.xcworkspace of the new sample project.

Follow the previous steps to import the HelloWorld framework and use the Logger class and printHelloWorld function in your Sample app.

That’s it! You’ve successfully created a universal framework in swift and you were able to distribute it using cocoa pods.

Hope this article is useful for beginners looking to create a Universal Framework in swift and to distribute it using cocoa pods! 😊

Let me know your feedback.

Thank you for reading! If you liked this article, please clap so other people can read it too :)

--

--