基于Cocaopods工程,快速实现Swift组件二进制

2,167 阅读4分钟

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

公司工程项目基于cocoapods完成了的组件化,为了能够提升编译速度,我们进行了组件二进制化,工程代码是swift代码。在这里总结下swift 和 OC 混编下的组件二进制化,这里主要介绍两种二进制组件:frameworkxcframework

制作framework

模版工程

我们通过 Cocoapods的模板,创建一个有Example的工程。在终端输入pod lib create Animal,语言选择 swift,并打开workspace工程。

pod lib create.pngpodspec文件中增加 Alamofire依赖,指定为静态framework,并将Build Settings中的Build Libraries for distribution设置为true

截屏2021-11-13 下午3.53.51.png

Animal/Classes目录下新建OC文件Monkey.h/m

@implementation Monkey
-(void)run {
    NSLog(@"猴子跑起来了.....");
}
@end

新建 Animal.swift,并引入AlamofireMonkey类

import Foundation
import Alamofire
open class Animal: NSObject {
    public func beginRun()  {
        let a = SessionManager()
        print("Alamofire ------- \(a)")
        print("开始跑步")
    }
    public func monkeyRun(){
        let monkey = Monkey()
        monkey.run()
    }
}

这样,我们就可以在主工程里面引入使用了

override func viewDidLoad() {

  super.viewDidLoad()
  let animal = Animal()
  animal.beginRun()
  animal.monkeyRun()
}

Shell脚本制作

接下来,我们将使用脚本借助Pods.xcodeproj的工程配置,一步完成framework的制作,核心步骤如下:

  • 1,编译真机设备的framework
xcodebuild -${WORK_TYPE} "${SRCROOT}/Pods.xcodeproj" -scheme $SCHEME_NAME -configuration ${CONFIGURATION} -sdk iphoneos
  • 2,编译模拟器设备的framework
xcodebuild -${WORK_TYPE} "${SRCROOT}/Pods.xcodeproj" -scheme $SCHEME_NAME -configuration ${CONFIGURATION} -sdk iphonesimulator -arch x86_64
  • 3,合并为通用framwork
lipo -create "${DEVICE_DIR}/${TARGETNAME}" "${SIMULATOR_DIR}/${TARGETNAME}" -output "${INSTALL_DIR}/${TARGETNAME}"
  • 4,拷贝模拟器设备真机设备下的swiftmodule文件,放到通用framworkswiftmodule目录下
cp -r ${SIMULATOR_SWIFTMODULE_DIR}/* "${INSTALL_DIR}/Modules/${TARGETNAME}.swiftmodule"

cp -R "${INSTALL_DIR}" "${FINAL_FRAMEWORK_PATH}"

buildFramework.sh放至Example目录下,运行脚本sh buildFramework.sh即可。

截屏2021-11-13 下午4.11.58.png

截屏2021-11-13 下午4.15.19.png

这样我们就完成了通用framwork的制作,并把所有架构下的swiftmodule文件和swiftInterface文件存放到了总的文件夹里面。

build脚本如下:

// buildFramework.sh
TARGETNAME='Animal'
WORK_TYPE="project" # 有效值 project / workspace (cocoapods项目)
SCHEME_NAME="Animal"
SCRIPT_PATH=$(cd `dirname $0`; pwd)
SRCROOT=${SCRIPT_PATH}/Pods
WORK_PATH=${SRCROOT}/${TARGETNAME}

rm -rf ${SCRIPT_PATH}/build

BUILD_ROOT=${SCRIPT_PATH}/build
CONFIGURATION="Release"

mkdir ${SCRIPT_PATH}/build
echo "🚀 开始创建${TARGETNAME}.framework"

INSTALL_DIR=${SRCROOT}/Products/${TARGETNAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${TARGETNAME}/${TARGETNAME}.framework
DEVICE_SWIFTMODULE_DIR=${DEVICE_DIR}/"Modules"/${TARGETNAME}".swiftmodule"
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${TARGETNAME}/${TARGETNAME}.framework
SIMULATOR_SWIFTMODULE_DIR=${SIMULATOR_DIR}/"Modules"/${TARGETNAME}".swiftmodule"

echo "🚀 开始编译真机设备"
xcodebuild -${WORK_TYPE} "${SRCROOT}/Pods.xcodeproj" -scheme $SCHEME_NAME -configuration ${CONFIGURATION} -sdk iphoneos

if [ "$?" != 0 ]
then
    echo "❎❎ 真机设备编译失败..."
    exit 0
fi

xcodebuild -${WORK_TYPE} "${SRCROOT}/Pods.xcodeproj" -scheme $SCHEME_NAME -configuration ${CONFIGURATION} -sdk iphonesimulator -arch x86_64

if [ "$?" != 0 ]
then
    echo "❎❎ 模拟器设备编译失败..."
    exit 0
fi

# 如果合并包已经存在,则替换
if [ -d "${INSTALL_DIR}" ]
then
    rm -rf "${INSTALL_DIR}"
fi

mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
# 使用lipo命令将其合并成一个通用framework
lipo -create "${DEVICE_DIR}/${TARGETNAME}" "${SIMULATOR_DIR}/${TARGETNAME}" -output "${INSTALL_DIR}/${TARGETNAME}"

# 拷贝 simulator 的swiftmodule
cp -r ${SIMULATOR_SWIFTMODULE_DIR}/* "${INSTALL_DIR}/Modules/${TARGETNAME}.swiftmodule"
if [ "$?" != 0 ]
then
    echo "❎❎ 拷贝失败..."
    exit 0
fi
# 拷贝 真机 的swiftmodule
cp -r ${DEVICE_SWIFTMODULE_DIR}/* "${INSTALL_DIR}/Modules/${TARGETNAME}.swiftmodule"
FINAL_FRAMEWORK_PATH=${SRCROOT}/../../
cp -R "${INSTALL_DIR}" "${FINAL_FRAMEWORK_PATH}"
echo "🚀  ✌️ ✌️ ✌️  ${TARGETNAME}.framework 制作成功"
echo "${TARGETNAME}.framework 路径:${FINAL_FRAMEWORK_PATH}"

二进制和源码切换

在完成二进制框架制作之后,我们通过podspec文件的subspec,来实现组件二进制依赖源码切换, 在podspec文件中,添加如下

截屏2021-11-13 下午4.26.25.png

此时完整的podspec文件如下

Pod::Spec.new do |s|
  s.name             = 'Animal'
  s.version          = '0.1.0'
  s.summary          = 'A short description of Animal.'
  s.description      = <<-DESC
                        Animal
                       DESC
  s.homepage         = 'https://github.com/1260197127@qq.com/Animal'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { '1260197127@qq.com' => '1260197127@qq.com' }
  s.source           = { :git => 'https://github.com/1260197127@qq.com/Animal.git', :tag => s.version.to_s }
  s.ios.deployment_target = '9.0'
  s.dependency 'Alamofire'
  s.static_framework = true
  s.pod_target_xcconfig = { 'BUILD_LIBRARY_FOR_DISTRIBUTION' => true }
  s.subspec 'Framework' do |sf|
    sf.vendored_framework = "Animal.framework"
  end
  s.subspec 'Source' do |sc|
    sc.source_files = 'Animal/Classes/**/*'
  end
  s.default_subspecs = 'Framework'
end

默认是使用 Framwork,如果想要切换源码二进制,可以在Podfile文件里面,进行切换

use_frameworks!
platform :ios, '9.0'
target 'Animal_Example' do
  #  pod 'Animal', :path => '../', :subspecs => ["Framework"] # 使用Framework
  #  pod 'Animal/Framework', :path => '../' # 使用Framework
  #  pod 'Animal/Source', :path => '../' # 使用源码
  pod 'Animal' , :path => '../' # 默认使用 framework
  target 'Animal_Tests' do
    inherit! :search_paths
  end
end

我们查看 Animal工程,此时引用的是 Animal.framework

截屏2021-11-13 下午4.37.25.png

至此,我们使用framwork来实现组件二进制和源码切换就完成了,相比于framework,在Xcode 11之后,有一个更好的二进制框架xcframework,苹果官方也是推荐使用xcframework的。

xcframework

xcframework有几个好处

1,可以用单个.xcframework文件提供多个平台的分发二进制文件。

截屏2021-11-13 下午4.49.35.png

2,可以按照平台划分,可以包含相同架构的不同平台文件。

截屏2021-11-13 下午4.50.45.png

制作 xcframework

同样借助cocoapods工程,我们使用shell脚本,一步完成xcframwrok的制作。 核心步骤有两步,

  • 1,在Release环境下,对不同平台分别进行archive(开启Build Libraries for distribution)。
xcodebuild archive -project "${SRCROOT}/Pods.xcodeproj" -scheme $SCHEME_NAME -configuration ${CONFIGURATION} -destination 'generic/platform=iOS' -archivePath "../archives/$SCHEME_NAME.framework-iphoneos.xcarchive" SKIP_INSTALL=NO

xcodebuild archive -project "${SRCROOT}/Pods.xcodeproj" -scheme $SCHEME_NAME -configuration ${CONFIGURATION} -destination 'generic/platform=iOS Simulator' -archivePath "../archives/${SCHEME_NAME}.framework-iphonesimulator.xcarchive" SKIP_INSTALL=NO
  • 2,使用 xcodebuild -create-xcframework创建xcframwork
xcodebuild -create-xcframework \
-archive "../archives/${SCHEME_NAME}.framework-iphonesimulator.xcarchive" \
-framework "${SCHEME_NAME}.framework" \
-archive "../archives/${SCHEME_NAME}.framework-iphoneos.xcarchive" \
-framework "${SCHEME_NAME}.framework" \
-output "../${SCHEME_NAME}.xcframework"

运行脚本sh buildXCFramework.sh,我们就可以得到xcframework文件。

截屏2021-11-13 下午5.08.11.png

使用xcframework

使用方式和framework是一致的,将vendored_framework指向Animal.xcframework即可。

截屏2021-11-13 下午5.09.30.png

常见问题

1 ,⚠️:x86_64-apple-ios-simulator.swiftsourceinfo' is either malformed or generated by a different Swift version. Note that it uses an unstable format and may leak internal project details, it should not be distributed alongside modules

如果我们使用.framework的形式,在swift版本更新时,会报这个警告,使用xcframework可以解决这个问题。

2,❌:Module compiled with Swift 5.1.xx cannot be imported by the Swift 5.4.xxx compiler:

swift进行大版本升级时,如果没有开启 Build Libraries for distribution,则会报这个错误,所以,在制作二进制时,将其设置为YES即可。

总结

我们分别探讨了使用frameworkxcframework两种方式,来制作swift二进制组件,然后通过subspec的形式,实现组件二进制源码的切换。

本文涉及到的代码Shell脚本,已上传至Animal,脚本文件在 Example目录下,有需要的可以自行下载。

如果觉得有收获请按如下方式给个 爱心三连:👍:点个赞鼓励一下。🌟:收藏文章,方便回看哦!。💬:评论交流,互相进步!