swift framework中引入第三方的framework或
说明
下面的框架是以swift为例创建的框架,引入的第三方框架有OC和swift的,在framework中使用第三方库的方法
因为公司的新需求,要在原本的框架之中再封装一层,本次就拿微信和支付宝的框架做一个demo。开始之前我们先对framework和.a这些库做一个说明。
1.静态库和动态库
库,其实说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用,一般有以下两种场景要用到分类分为两种:
- 某些你想给别人用,但是不想别人看到自己源码,就需要以库的形式进行封装,只暴露出头文件就行。
- 对于一些不会进行很大改动的代码,单独封装成模块,这样做可以节省时间,代码管理也很方便
因为库已经是编译好的二进制文件,编译的时候只需要link一下,link的时候有两种不同的形式,静态和动态,与之对应的就是静态库和动态库。
静态库
平时用到的第三方的SDK基本上都是静态库,静态库有一下几个特点:
1.在app项目编译的时候会被拷贝一份编译到目标程序中,相当于将静态库嵌入了,所以得到的APP二进制文件会变大
- 2.在使用的时候,需要收到导入静态库所依赖的其他类库,比如这个demo中我们要使用到的类库,使用的时候只能手动导入,有的SDK需要导入十几个其他依赖库,😂😂😂 支付宝SDK依赖库
3.导入静态库可以减少应用对外界的依赖,如果导入的是第三方动态库,动态库找不到的话就会奔溃,
4.静态库的最大的优点就是减少耦合性,因为静态库中是不可以包含其他静态库的,使用的时候要另外导入它的依赖库,可以最大限度的保证了每一个静态库都是独立的,不会重复引用。
动态库
这个使我们常用的动态库,使用最多的就是UIkit.framework和Fundation.framework,这两个都是动态库。所有.dylib和.tbd结尾的都属于动态库。动态库有以下几个特点:
- 1.平时使用的系统库都放在iOS系统中,在你打包应用程序的时候这些库不会拷贝到你的程序中,当需要使用的时候才会动态的从iOS系统中加载他们,因为这个原因,动态库也称为共享库。编译的时候才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译。
- 2.这些库是所有应用公用的,换一种说法就是节省了应用安装包的体积,这就是区别静态库的一个重要的特点,因为静态库使用一次就要拷贝一次,比较消耗资源。
- 3.动态库在制作的时候可以直接包含静态库,也能自动link所需要的依赖库
- 4.使用动态库的时候不需要再次link依赖库,即导即用。需要注意的是在导入自己制作的动态库时,需要在Embedded Binaries中导入,不然会报image not found。此时这个动态库会跟静态库一样被拷贝到目标程序中进行编译
在动态库选择上要考虑能不能上架的问题,目前第三方的SDK也都是静态库,至于动态库能不能上架这就不是很清楚了,毕竟苹果也不想你动态的在系统中下载代码。
2. framework 、.a 、.dylib/.tbd
1.framework
framewoz主要是由Headers、binary文件和.bundle这三部分构成,除此之外还有info.plist和Modules,后两者主要记录framework的版本之类的信息,一般可以删掉。
*Headers
包含在我们制作的framework的时候回暴露的头文件,所有被暴露的.h都放在这里
*binary文件
整个framework的核心,所有的代码都被编译成了这样的一坨二进制文件,这里要注意的就是添加的依赖库不会被编译进来,用的时候还需要重新link其他的依赖库
.bundle
资源文件都打包放在这里,在制作framework的时候不可以把图片直接放在项目中,否则制作好之后的图片是一张一张出现在项目中,很是混乱,这时候需要新建一个bundle将图片、xib等一些资源放进去,
提示
图片放进bundle中之后不能直接用[UIImage ImageWithName:]
,要找到bundle包再去获取图片。
另外说明一下framework是静态库还是动态库的问题
其实framework既可以是动态库也可以是静态库,在取决于编译的时候Mach-O(就是那个二进制文件)是动态库还是静态库,如下图所示。
静态库和动态库选择 framwwok本质上并不是一个库,只是苹果为了方便开发者提供的一种库的额打包方式,framework会将Mach-O文件,头文件和资源包全都包含进来,不在需要手动整理。所以总的来说,Framework是库的打包形式,既可以是动态的也可以是静态的。2. .a静态库
这类静态库与framework基本类似,不同的是在打包成.a文件的同时,还需要提供头文件,使用时相对于framework比较麻烦,例如微信执法SDK使用的是.a,而支付宝使用的是framework的形式打的包。.a打包不够方便,而framework编译完成暴露的头文件都已经放好了
3. .dylib/.tbd 动态库
这类动态库经常用到,基本上就是系统提供的,就算是自己制作了,也肯定上不了架。
3.framework 动态库和静态库的打包
静态库和动态库的制作过程基本一样,包括暴露的头文件,唯一不同的就是上面图中显示的Mach-O Type的编译形式。demo中介绍framework静态库依赖其他第三方静态库,支付宝的framework
1.新建工程
新建framework工程
右边的static Library制作出来的是.a静态库
2.导入所有要打包的文件和其他的第三方的静态库
正常导入要打包的文件就行了,在导入第三方静态库的时候要注意,不要选择添加到target中,如果添加了要去target中把里面的第三方静态库删除(只要导入,不要添加进target)
不要勾选target
修改framework支持的最低版本,添加依赖库
修改支持的版本
添加依赖库
这个时候编译会报错
编译报错
这是因为添加的两个.dylib文件找不到,删除这两个库,重新添加。这时候要选择add other选择添加,在弹出的窗口中按快捷键shift + command + G 调出finder的前往窗口,输入/usr/lib,然后添加相应的dylib动态库.如下图
add other shift + command + G 选取文件
3.暴露文件,在targets->Build Phases->headers中添加public文件
public文件
接下来开始编译,就可以制作一个静态包出来。
4.接下来我说一下在整合中遇到的坑,
因为在项目中拿到添加了framework,但是怎么都引用不了,里面的头文件和其他的都访问不到,因为frameworkj就是在.a静态库基础上更上一层的封装,包含了资源,二进制文件和头文件并生成了统一格式,方便用户调用,并不是重新生成另外一种格式,只是对老的格式进行了一层更规范的封装,统一管理资源文件,头文件和库的二进制文件。那么我们就可以把别人的framework变成一个.a静态库文件。
为了方便测试,在真机和模拟其中来回的切换,我们要在打包的framework之间来回切换并重新编译,详情请看合并真机和模拟器的方法
下面说一种比较简单方便的办法
1.新建一个target
新建target
2.选择aggregate新建,命名为UniversalSDK
aggregate创建
3.选中UniversalSDK 在build Phases中点击左上角的+号,如下图
添加run script
4.将下面的代码复制到shell下面的框中
#!/bin/sh
UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
# Step 1. Build Device and Simulator versions
xcodebuild -target ${PROJECT_NAME} ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target ${PROJECT_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}"
# Step 5. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
在编译的时候选择UniversalSDK直接在目录中生成一个SDK,这是真机和模拟器都能用的。测试的时候为了避免重复打包,直接将项目拖到测试工程中,如下图:
sdk直接引用
直接引用文档
这样就可以在项目中直接使用SDK,而且每次修改SDK不用重新引入,编译一下即可在测试工程中使用。