Create Universal Framework and use in iOS project using Xcode 12
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:
- Code encapsulation
- Code modularity
- 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.
- 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.
- 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 😊