Create Universal Framework and use in iOS project using Xcode 12

Rohit Tamkhane
6 min readOct 17, 2020

--

What is framework?

A framework is a modular and reusable set of code. It can be built once and used infinite number of times.

Frameworks have three major purposes:

  1. Code encapsulation
  2. Code modularity
  3. Code reuse

How to build a framework

Let’s start with creating a custom framework and make it universal so that it can be run on simulator as well as device.

  1. Open Xcode and create a new project. Select Framework from Framework and Library section and click Next.

2. Enter product name and fill other details

Once you fill all the details then press Next. Your project is ready to build like below

If you build the project, it will produce RTTestFramework.framework as a product which will support a simulator or device based on the option chosen by developer.

3. Add a new file to our project. Right click on project folder and choose new file

Select Swift file from Source and click Next.

Give the file name you want and click on create. I have named my file Utility.

4. In the new Swift file that we have created, we need to add the following code:

import UIKitpublic func hexStringToUIColor(hex:String) -> UIColor {var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()if (cString.hasPrefix("#")) {cString.remove(at: cString.startIndex)}if ((cString.count) != 6) {return UIColor.gray}var rgbValue:UInt64 = 0Scanner(string: cString).scanHexInt64(&rgbValue)return UIColor(red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,blue: CGFloat(rgbValue & 0x0000FF) / 255.0,alpha: CGFloat(1.0))}public func displayAlert(viewController: UIViewController, title: String, message: String) {let alertCotroller = UIAlertController.init(title: title, message: message, preferredStyle: .alert)alertCotroller.addAction(UIAlertAction.init(title: "Ok", style: .default, handler: { (action) in}))viewController.present(alertCotroller, animated: true, completion: nil)}

These are two simple function which are used for getting UIColor from hex string and display alert to user, but because we want to use this class outside of our framework we need to mark it is as public.

5. Select Project Target → Edit Schema → Archive → Post-actions → Press “+” → New Run Script Action.

Copy paste below script code

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-Universal# Make sure the output directory existsmkdir -p "${UNIVERSAL_OUTPUTFOLDER}"# Build Device and Simulator versionsxcodebuild -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# Copy the framework structure (from iphoneos build) to the universal foldercp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"# 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"# 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}"# Copy the framework to the project directorycp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"# Open the project directory in Finderopen "${PROJECT_DIR}"fi

Once you are complete with copy paste above script into your project, it will look similar to this -

Press close and select archive option from product tab as shown in below picture

After completion of archive process the above script will be execute and generate the universal framework and will open in project directory itself.

Now your universal framework is ready.

How to use this framework in other project

Let’s start with using our own framework in a project.

  1. Open Xcode and create a new project. Select App from Application section.

2. Enter product name and fill other details

Once you fill all the details and press next.

3.Copy/Drag the Universal Framework into the project.

While coping the framework in Project, check “Copy items if needed”.

4. Select the Project, Choose Target Project Name Select General Scroll to “Embedded Binaries”. Press “+” and Add the framework.

Now we have completed the setup we can use the framework. I am going to use it in the viewDidLoad in my ViewController file in FrameworkDemo. Add the following import to the top of the file, below import UIKit:

import RTTestFramework

Next we need to add below code in viewDidLoad which change background color of view with assigned hex string color.

view.backgroundColor = hexStringToUIColor(hex: "#543444")

Add display alert button in storyboard and add button action in view controller as shown in picture

Now add below code in actionOnDisplayAlert function to display alert on button click.

displayAlert(viewController: self, title: "Alert Title", message: "Alert Message")

Build and run the app. When you do, you will get following output.

Remove Unused Architecture

Apple doesn’t allow the application with unused architectures to the App Store so we need to remove unused architectures.

We need to add run script in the application that uses our custom universal framework.

Select the Project, Choose Target → Project Name → Select Build Phases → Press “+” → New Run Script Phase → Name the Script as “Remove Unused Architectures Script”

Copy & paste below script -

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"# This script loops through the frameworks embedded in the application and 
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORKdoFRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"EXTRACTED_ARCHS=()for ARCH in $ARCHSdoecho "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")doneecho "Merging extracted architectures: ${ARCHS}"lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"rm "${EXTRACTED_ARCHS[@]}"echo "Replacing original executable with thinned version"rm "$FRAMEWORK_EXECUTABLE_PATH"mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"done

It look similar to below picture-

This script removes the unused simulator architectures only while uploading the Application to the App Store.

Now Custom Universal iOS Framework is Complete.

Hope this article is useful for people looking to create custom Universal iOS framework.

Thank you 😊

Code - https://github.com/rohittamkhane/Framework

--

--

Rohit Tamkhane
Rohit Tamkhane

No responses yet