The background that

The company required other project teams to provide an entry to access the interface of our project, so we decided to adopt the form of framework, package our project into a framework for other project teams, and then provide the corresponding interface call. However, due to historical reasons, another project team couldn’t rely on third parties through cocoaPod, so we had to integrate the third party source code into our framework. More on how to make a framework that relies on third-party libraries through cocoaPod later.

Knowledge sorting before production

What is a framework

The framework is essentially a library that can be made available to others but hides the implementation inside. The. Framework of the system is a dynamic library, which we build ourselves. The framework is a static library.

Static and dynamic libraries

Static libraries are fully copied to executable files when they are connected, and there are multiple redundant copies when they are used many times. Exist in the form of. A and. Framework dynamic library connection does not copy, the program is dynamically loaded into the memory by the system when running, for the program to call, the system only load once, multiple programs share, saving memory. It exists in the form of. Framework,.dylib,.tbd

The difference between.a and. Framework

A file cannot be used directly, but at least a. H file must be used together. The. Framework file can be used directly

Next comes the creation process

1 Create a Demo project

The Demo project is used to run and test the framework, because the framework is not a project and cannot be run directly

2 create a framework

TARGETS -> +

3 the configuration framework

Deployment Info Minimum system requirements

Of course, it is better to recommend low, (example ios9)

Build Active Architecture Only Builds the Active Architecture Only

If set to NO, not only the current schema will be compiled. If set to Yes, only the currently selected schema will be edited. For example, if you select iPhone 13 emulator to compile, the compiled framework will only be used by iPhone 13 emulator

Excluded Architecture

This is to remove the duplicate arm64 architecture, because the real machine and emulator compile, the framework in the arm64 framework is the same, which will cause the merge failure, so remove the arm64 architecture in the emulator

IOS instruction set knowledge

armv6

IPhone iPhone2 iPhone3G first generation and second generation iPod Touch

armv7

iPhone4 iPhone4S

armv7s

iPhone5 iPhone5C

arm64

iPhone5S iPhone6 iPhone6+

Instructions are backward compatible, such as the iPhone5s CPU which supports arm64, but it is also armv7s compatible, although if the program is compiled using armv7s instructions, it may not make the most of its 64-bit features.

Architecture

The device to which the program is compiled (armV7, armV7s…) Compile-time generates proprietary installation packages for different instruction sets (devices). Different devices will execute the command set corresponding to the device, such as iPhone5s will preferentially execute arm64 (if available)

Dead Code Stripping

Compilation option optimization is used to Strip the code that is not actually used in the executable binary compiled by the program to slim down the framework package. However, for the framework, it should be set to NO to prevent the code and debugging symbols from being stripped.

The Mach – O Type Type

Set the type to static library

Build Configutation Compiles the configuration

Set to release

5 Developing code

Swift does not expose interfaces like OC. To call interfaces to other projects in SWIFT, prefix classes, methods, or properties with public or open.

Swift Permission control letters

open

Maximum permission, can be accessed by external modules, overriding public

Internal can be accessed by external projects

Default file creation permissions can be accessed in this project private

It can only be accessed within the created file

For project reasons, the third-party framework can only be introduced through source code. Take SnapKit as an example, drag the source code of SnapKit into the project

At the same time create a test file gpkitController.swift, targets select GPKit

// // gpkitcontroller. swift // GPKit // // Created by Darren on 2022/3/25. // import UIKit /** GPKit controller */ public class GPKitController: UIViewController { public override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white Navigationitem. title = "GPKit" let label = UILabel() label.text = "I am the controller inside GPKit" label.textColor =.red view.addSubview(label) label.snp.makeConstraints { make in make.center.equalToSuperview() } } }Copy the code

6 Add compile merge script

The compilation of the framework is divided into simulator compilation and real machine compilation, and the framework we provide to others to use is generally run by the simulator and real machine, so we must merge the two versions of the framework into a general framework

targets -> +

There is no merge script code using cocoaPod

#! /bin/sh # SDK UNIVERSAL_OUTPUTFOLDER="${SRCROOT}/Products/" ${PROJECT_NAME}. Xcodeproj # if I use cocoaPod, ${PROJECT_NAME}. Xcworkspace WORKSPACE_NAME=${PROJECT_NAME}. Xcodeproj # create output path folder mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" Rm -rf "${UNIVERSAL_OUTPUTFOLDER}/${SDK_NAME}. Framework "${SDK_NAME}" -configuration ${CONFIGURATION} -sdk iphoneos ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build # Build simulator framework xcodebuild-target "${SDK_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean Cp -r "${BUILD_DIR}/${CONFIGURATION} -iphoneOS /${SDK_NAME}. Framework" "${UNIVERSAL_OUTPUTFOLDER}" # copy the swift module of the simulator framework to the path of the final output SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SDK_NAME}.framework/Modules/${SDK_NAME}.swi ftmodule/." if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" ${UNIVERSAL_OUTPUTFOLDER}/${SDK_NAME}. Framework /Modules/${SDK_NAME}. Swiftmodule" ${UNIVERSAL_OUTPUTFOLDER}/${SDK_NAME}. Framework /${SDK_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SDK_NAME}.framework/${SDK_NAME}" ${BUILD_DIR}/${CONFIGURATION}- iphoneOS /${SDK_NAME}. Framework /${SDK_NAME} dir_path="${UNIVERSAL_OUTPUTFOLDER}/${SDK_NAME}.framework/" for file in ls $dir_path do if [[ ${file} =~ ".xcconfig" ]] Then rm -f "${dir_path}/${file}" fi done ${UNIVERSAL_OUTPUTFOLDER}"Copy the code

Use cocoaPod’s merge script code

If the.xcworkspace file is created through cocoaPod, the script code is as follows

#! /bin/sh # SDK UNIVERSAL_OUTPUTFOLDER="${SRCROOT}/Products/" WORKSPACE_NAME=${PROJECT_NAME}. Xcworkspace # Create an output path folder mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" # Remove the framework rm -rf generated last time ${UNIVERSAL_OUTPUTFOLDER}/${SDK_NAME}. Framework "${WORKSPACE_NAME}" -scheme "${SDK_NAME}" -configuration ${CONFIGURATION} -sdk iphoneos ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" ${WORKSPACE_NAME}" -scheme "${SDK_NAME}"  -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build # Copy the generated real framework to the final output path cp -r "${BUILD_DIR}/${CONFIGURATION}- iphoneOS /${SDK_NAME}. Framework ""${UNIVERSAL_OUTPUTFOLDER}" # copy the swift module of the simulator framework to the final output path SIMULATOR_SWIFT_MODULES_DIR="${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SDK_NAME}.framework/Modules/${SDK_NAME}.swi ftmodule/." if [ -d "${SIMULATOR_SWIFT_MODULES_DIR}" ]; then cp -R "${SIMULATOR_SWIFT_MODULES_DIR}" ${UNIVERSAL_OUTPUTFOLDER}/${SDK_NAME}. Framework /Modules/${SDK_NAME}. Swiftmodule" ${UNIVERSAL_OUTPUTFOLDER}/${SDK_NAME}. Framework /${SDK_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${SDK_NAME}.framework/${SDK_NAME}" ${BUILD_DIR}/${CONFIGURATION}- iphoneOS /${SDK_NAME}. Framework /${SDK_NAME} dir_path="${UNIVERSAL_OUTPUTFOLDER}/${SDK_NAME}.framework/" for file in ls $dir_path do if [[ ${file} =~ ".xcconfig" ]] Then rm -f "${dir_path}/${file}" fi done ${UNIVERSAL_OUTPUTFOLDER}"Copy the code

7 generated framework

To execute the script, simply select GPKitAggregate and run

The resulting directory is as follows

8 test framework

Create a new project and drag the generated framework into it to create a jump point

Then run it respectively with the simulator and the real machine. If both can run successfully and jump, the framework is successfully made

9 Third-party reuse

The test creates a new framework called GPKit1, which also references the source code of SnapKit, and then imports GPK and GPKit1 into the test project at the same time, and jumps to the corresponding page respectively