Skip to content

Instantly share code, notes, and snippets.

@gauravkeshre
Last active January 13, 2019 19:47
Show Gist options
  • Save gauravkeshre/32e2dc4d78adc6a4f5f781f30a0a11a6 to your computer and use it in GitHub Desktop.
Save gauravkeshre/32e2dc4d78adc6a4f5f781f30a0a11a6 to your computer and use it in GitHub Desktop.
Manage first party dependencies with Universal Dynamic Framework. Without Cocoapod and Carthage

Manage first-party dependencies with Universal Dynamic Framework without using cocoapods or carthrage

Requirement

Should be able to setup your first party dependencies (dynamic frameworks) and the updates must reflect without re-integrating the new .framework repeatedly

Considerations

cocoapod, carthage are great when you have third party or well-defined first party dependencies, without doubt. But there will be instances when you don't want to create a git repository for a dependency or the dependency just sits in your project and you just want the.framework file instead of subproject.

Pre Conditions

  1. You have a first party framework
  2. You don't want to create a new dedicated repository for them (so using Carthage / Cocoapods are out of question [Citation Needed])
Optional Pre Conditions
  1. You want to keep the Dependencies under your main project directory
  2. You do not want to add a sub project to your workspace just for a framework

Solution

Note : This will still require you to setup things for the first time



One Time Setup

1. Setup Dependency Project

  1. Open Dependecy.xcodeproject
  2. Select Target
  3. [ ⌘ + ⌥ + i] Command + alt + i.
  4. Archive > Post-actions > + > New run script action
  5. Provide build settings from : SELECT YOUR TARGET
  6. copy and paste this script in the script area.
Build Settings Changes
  1. Select Target > Build Settings > Skip Install
  2. Ensure that it is set to NO

2. Customize the script

  1. While in the post actions script
  2. Change the DESTINATION_ABSOLUTE_PATH to your main project's root path

3. Archive the dependency project

  1. Archive the dependency project
  2. Close the organizer ( we don’t want to export anything)
  3. Wait for 1 - 2 sec while a fat .framework file is generated and pasted in DESTINATION_ABSOLUTE_PATH path

4. Integrate the .framework file in the main project

  1. Drag the Dependency.framework to main project's embedded binaries section.
  2. Compile and check if everything works in main project.



Every time you make changes in dependency project

  1. Archive the dependency project
  2. That's it

This will create a fat file and replace the existing .framework file in project rood and update the references appropriately.

Caveats ⚠️

Apple has started complaining if app contains simulator architectures during distribution.

One solution, if you wish to do it only once. But be careful though as after doing following steps, you will not be able to run app on simulator. Do it just before deploying the app on Testflight/App-store.

Go inside the your MainProjectFramework.framework folder of your project from terminal. Run following commands:

lipo -remove i386 ProjectFramework_SDK -o ProjectFramework_SDK 
lipo -remove x86_64 ProjectFramework_SDK -o ProjectFramework_SDK

Sources

Note

Please feel free to help improve this document.

Analytics

exec > /tmp/${PROJECT_NAME}_archive.log 2>&1
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# Change this path to your main project's root directory.
# It will put framework in dependency project directory by default
DESTINATION_ABSOLUTE_PATH=${PROJECT_DIR}
if [ "true" == ${ALREADYINVOKED:-false} ]
then
echo "RECURSION: Detected, stopping"
else
export ALREADYINVOKED="true"
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
echo "Building for iPhoneSimulator"
xcodebuild -workspace "${WORKSPACE_PATH}" -scheme "${TARGET_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' ONLY_ACTIVE_ARCH=NO ARCHS='i386 x86_64' BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" ENABLE_BITCODE=YES OTHER_CFLAGS="-fembed-bitcode" BITCODE_GENERATION_MODE=bitcode clean build
# Step 1. Copy the framework structure (from iphoneos build) to the universal folder
echo "Copying to output folder"
cp -R "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${FULL_PRODUCT_NAME}" "${UNIVERSAL_OUTPUTFOLDER}/"
# Step 2. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory
SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule/."
if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then
cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Modules/${TARGET_NAME}.swiftmodule"
fi
# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory
echo "Combining executables"
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${EXECUTABLE_PATH}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${EXECUTABLE_PATH}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${EXECUTABLE_PATH}"
# Step 4. Create universal binaries for embedded frameworks
#for SUB_FRAMEWORK in $( ls "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks" ); do
#BINARY_NAME="${SUB_FRAMEWORK%.*}"
#lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SUB_FRAMEWORK}/${BINARY_NAME}" "${ARCHIVE_PRODUCTS_PATH}${INSTALL_PATH}/${TARGET_NAME}.framework/Frameworks/${SUB_FRAMEWORK}/${BINARY_NAME}"
#done
# Step 5. Convenience step to copy the framework to the project's directory
echo "Copying to project dir"
yes | cp -Rf "${UNIVERSAL_OUTPUTFOLDER}/${FULL_PRODUCT_NAME}" "${DESTINATION_ABSOLUTE_PATH}"
open "${PROJECT_DIR}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment