In daily development, we may need to package some SDK or use some SDK that someone else has packaged. Here’s a summary of the SDK packaging process.

The basic concept

There are generally two kinds of SDKS we encounter:.framework and.a files. First, we need to understand the differences between these two types of files.

Before we get to the difference between.framework and.a files, we need to understand two other concepts: static libraries and dynamic libraries.

Static library

A static library is a static linked library that is copied directly to the target program during compilation. The static library code is part of the target program.

The dynamic library

The dynamic library is not copied to the target application at compile time, only the reference to the dynamic library is stored in the target application, and the dynamic library is not loaded until the App starts.

Static library VS dynamic library

  • Static libraries copy code into the target program at compile time, resulting in an increase in the target program’s size. When used multiple times, there are many redundant copies in memory.
  • Dynamic library During cold startup of App, dynamic link library needs to be loaded to adjust the Rebase pointer and bind the bind symbol, which will increase the startup time of App. It is dynamically loaded into memory by the system for App call. The system only loads it once, and multiple programs share it, saving memory.
  • Static libraries in iOS form:.a and.framework
  • Dynamic libraries in iOS form:.dylib and.framework

Apple’s dynamic library history

Before iOS 8, iOS platform does not support custom dynamic libraries, developers can only use Apple’s own dynamic libraries such as UIKit. Framework, Foundation. Framework. The reason for this restriction is for security reasons, because iOS apps run in a sandbox, and different programs cannot share code with each other. Dynamic downloading of code is explicitly forbidden by Apple, and since there is no way to take advantage of dynamic libraries, there is no need for dynamic libraries. Prior to iOS 8, there were a few. Framework files provided by third parties, but they were essentially static libraries wrapped in a few ways that made it easier to use. With the release of iOS 8/Xcode 6, dynamic library support was added to the iOS platform, allowing developers to create and use dynamic libraries conditionally. This dynamic library is called Cocoa Touch Framework, but this dynamic Framework is quite different from the system Framework. The framework of the system does not need to be copied into the target application at compile time, whereas the Cocoa Touch framework is put into the App bundle and runs in the sandbox when the App is packaged and submitted. Even if different apps use the same framework, multiple frameworks will be signed, packaged and loaded separately. Therefore, Apple also calls this framework as Embedded Framework.

Cocoa Touch Framework was introduced to solve two problems:

  1. Expansion development since iOS 8
  2. Swift in its early days did not support compiling to static libraries

.a VS .framework

  • A is a pure binary file. In addition to binary files, there are resource files in the framework
  • The. A file cannot be used directly, so you need to import. H files together with. Framework files that contain
  • A files are static libraries. The. Framework can be either static or dynamic

tutorial

There are a lot of tutorials online on how to make.a files or.framework, so I’m not going to go into that. Here are a few key points.

architecture

Apple’s architecture falls into two broad categories: emulator architecture and real machine architecture

  1. Simulator architecture
  • I386 32-bit architecture 4S ~ 5
  • X86_64 64-bit architecture 5S to current models
  1. Real machine architecture
  • Armv7 32-bit architecture 3GS to 4S
  • Armv7s special architecture 5 ~ 5C (there are problems with this architecture, some programs get faster, some programs get slower)
  • Arm64 64-bit architecture 5S to current models

Synthesis of architecture

Packages compiled using emulators are simulator architectures, packages compiled using real machines are real architectures. You can use lipo-info to view the schema of the current package. The advantage of the combination of real machine and simulator architecture is that it is very convenient to debug, but the disadvantage is that the volume will become larger. Generally speaking, THE SDK needs a composite architecture to be convenient for users to use. Compositing architecture commands: A device. A-output name. A construct command for the framework file is also used to construct the lipo-create executable file inside the framework file.

The script package

Manual packaging is fine for our needs, but scripting packaging offers several advantages:

  1. Improve efficiency, the original tedious packaging process, only need to execute a script can be completed
  2. Unified and standardized, tedious operation process, relying on individuals to complete, it is inevitable that there will be errors, the use of scripts can ensure accuracy
  3. Easy to use, using script packaging, even newcomers can be very easy to use, reduce the cost of communication

Since script packaging has so many advantages, let’s summarize the process of implementing script packaging:

Script Packaging

  1. usingxcodebuildPackage the simulator architecture and the real machine architecture separately
  2. usinglipo -createMerge the simulator and real architecture
  3. If it is a framework merge, you need to copy the merged binary executable into the framework
  4. Copy the file to the specified directory and open the folder

A detailed script

# the project name
POD_PROJECT_NAME=${PROJECT_NAME}
CONFIGURATION=Release
# custom used to store the final merged framework
UNIVERSAL_OUTPUTFOLDER=${SRCROOT}/Products/${CONFIGURATION}-universal

WORKSPACE_NAME=${PROJECT_NAME}.xcworkspace
SCHEME_NAME=${PROJECT_NAME}

#clean build is to clean the original build first
xcodebuild -workspace ${WORKSPACE_NAME} -scheme ${SCHEME_NAME} -sdk iphonesimulator -configuration "${CONFIGURATION}" clean build
xcodebuild -workspace ${WORKSPACE_NAME} -scheme ${SCHEME_NAME} -sdk iphoneos -configuration "${CONFIGURATION}" clean build
echo "${WORKSPACE_NAME}"
echo "${PROJECT_NAME}"

Remove the original first
rm -rf "${UNIVERSAL_OUTPUTFOLDER}"
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

Clear the Build folder
rm -rf "${BUILD_DIR}"

Build OS libraries
xcodebuild build -workspace ${WORKSPACE_NAME} -scheme ${POD_PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"

Create simulator i386 library
xcodebuild build -workspace ${WORKSPACE_NAME} -scheme ${POD_PROJECT_NAME} -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"

Rename the i386 library
mv ${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${POD_PROJECT_NAME}/${POD_PROJECT_NAME}.framework ${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${POD_PROJECT_NAME}/${POD_PROJECT_NAME}i386.framework

Build the simulator x86_64 library
xcodebuild build -workspace ${WORKSPACE_NAME} -scheme ${POD_PROJECT_NAME} -configuration ${CONFIGURATION} -sdk iphonesimulator -arch x86_64 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}"

# Merge simulator i386 and X86_64 architectures
lipo -create  "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}/${PROJECT_NAME}i386.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}/${PROJECT_NAME}.framework/${PROJECT_NAME}" -output "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}/${PROJECT_NAME}.framework/${PROJECT_NAME}"

Because of the framework merge, Lipo just merges the last binary executable, so we need to copy the rest ourselves
Copy the file to the specified directory
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework"

# merge the simulator (i386/ X86_64) and the real machine (armV7 / ARM64) architecture
lipo -create  "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}/${PROJECT_NAME}.framework/${PROJECT_NAME}" -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}"

open "${UNIVERSAL_OUTPUTFOLDER}"
Copy the code

Using Aggregate provides a shortcut

  1. Create an Aggregate Target under the current project

  1. Add the Run Script

  1. Enter the Script contents in Run Script Phases

  1. Compile Aggregate Target to complete script packaging