Hide source code in Dynamic Framework

Dynamic framework
Hello, dear reader.

Often when you are creating a framework, a best practice is to hide the source code so that no one view the framework. These are some of the challenges that I face when creating  my own framework.

When adding the framework of the folder “Debug-iphoneos“, it compiled as expected on a device. However,  in an emulator it displayed the following errors:

Command PhaseScriptExecution emitted errors but did not return a nonzero exit code to indicate failure

/: unable to load standard library for target ‘arm64-apple-ios12.1-simulator’ (Depending on the emulator where you were running)

New Project

We will star creating a Framework project.  This is simple to do.

Now in the base of the project, we are going to create a shell script (ScriptFramework.sh), with the following code:

if [ “true” == ${ALREADYINVOKED:-false} ]

then

echo “RECURSION: Detected, stopping”

else

export ALREADYINVOKED=“true”

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-iosuniversal

# make sure the output directory exists

mkdir -p “${UNIVERSAL_OUTPUTFOLDER}”

# Step 1. Build Device and Simulator versions

xcodebuild -target “${TARGET_NAME}” ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneosBUILD_DIR=“${BUILD_DIR}” BUILD_ROOT=“${BUILD_ROOT}” clean build

xcodebuild -target “${TARGET_NAME}” -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR=“${BUILD_DIR}” BUILD_ROOT=“${BUILD_ROOT}” clean build

# Step 2. Copy the framework structure (from iphoneos build) to the universal folder

cp -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 directory

SIMULATOR_SWIFT_MODULES_DIR=“${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/.”

if [ -d “${SIMULATOR_SWIFT_MODULES_DIR}” ]; then

cp -R “${SIMULATOR_SWIFT_MODULES_DIR}” “${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule”

fi

# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory

lipo -create -output “${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}” “${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}” “${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}”

fi

Now, the new shell file has to set the executable as the ScriptFramework.sh file that we just created:

We are selecting the project in XCode we select Build Phases, Click on + button, and choose New Run Script Phas, then Insert this shell command:

${SRCROOT}/ScriptFramework.sh

We create our class where we have the functions that we want in our Framework. For this example, I am going to use a sum function to receive two integers and return an integer. Additionally, creating a function to print in the console any text that receives.

import Foundation

public class Util :NSObject{

 private override init() {
    super.init()
 }

  public class func sum(num1 :Int, num2 :Int) -> Int{
      return num1 + num2
 }

   public class func printConsole(text :String){
        print(text)
   }
}

Then we just select Project > Build and right click on the Framework and Show in Finder to see that it now shows us 3 folders. We will only be using the Framework in the Debug-iosuniversal folder.

Once we have our Framework working in both the Emulator and Device, we are able to distribute it without reaching our source code in our Framework.

Now we are going to create a demo project to show how to use the functions. 

Now we can use our Framework in any class of our project. This is an example of how we might use it:

import UIKit
import MyFramework

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let total :Int = Util.sum(num1: 23, num2: 12)
        Util.printConsole(text: “Total: “)
        print(String(total))
    }
}

We hope you find this tutorial useful. Please contact us with any question or comment.
guillermo.garcia@inmediatum.com

Write a comment