Swift + framework 的制作(基于project)

1.framework开始前的 理论

1).初衷

开始的理由千千万,但殊途同归.
(1) 提供给公司外部的人员使用, 不希望暴露内部"高大上"的实现
(2) 公司内部多个项目公用资源, 使用方便
(3) 模块化、分工合作, 架构组完成基础的实现, 功能开发工程师只需要关注功能实现, 不需要关注底层实现
(4) 提高编译速度,减少少量的改动引起的大量重复的编译
......

2).动态库,静态库

动态库存在形式: .framework, .dylib, .tbd

动态库是引用关系,编译时不会被拷贝到程序中,程序运行时 由系统动态加载

静态库存在形式: .a 和 .framework

静态库编译时会被完整拷贝一份到目标程序中

动态库与静态库详细的区别 参考:
https://my.oschina.net/shoutan/blog/786636

2.创建framework

1).创建工程

模板选择Cocoa Touch Framework

 
Swift + framework 的制作(基于project)_第1张图片
1创建framework.png

2).编写自定义代码

对需要共享的内容分模块添加

 
Swift + framework 的制作(基于project)_第2张图片
2编辑自定义代码.png

!建库时不得不注意的就是 swift的访问级别

Swift 提供了三种访问级别。这些访问级别相对于源文件中定义的实体,同时也相对于这些源文件所属的模块。
其中,函数的访问级别需要根据该函数的参数类型访问级别、返回类型访问级别得出。如果根据参数类型和返回类型得出的函数访问级别不符合上下文,那么就需要明确的申明该函数的访问级别。

代码中的所有实体,如果你不明确的定义其访问级别,那么它们默认为internal级别, 所以, 只是在一个工程中使用, 可以不用声明访问类型, 该工程 均可访问。

swift 三个等级的访问控制权限,可以简单的通过下面的规则来进行选择:

  • Public: 对 App 或其他 framework 可见。
  • Internal: 对该 framework 可见
  • Fileprivate: 对该编译文件可以见
  • Private: 对该类可见

具体参考文档: http://wiki.jikexueyuan.com/project/swift/

3.编译 真机 / 模拟器 的 包

1).编译

对工程进行编译(command + B), 找到project->products->选中EKWBaseUIKit.framework->Show In Finder, 如下图:

 
Swift + framework 的制作(基于project)_第3张图片
3编译得到framework.png

2).查看库文件

可以看到生成的 真机和模拟器下的两个包文件(当前设置的模式为release模式, 所以为release模式下的两个包文件), 如下图:

 
编译得到的库文件.png

3). 查看库文件支持框架

在终端通过lipo -info 静态库名字可以查看动态库支持的架构, 如下:

 
Swift + framework 的制作(基于project)_第4张图片
4lipinfo.gif

可以看到当前支持的框架包含: 有armv7arm64

目前iOS设备包含的架构:
模拟器:iPhone4s-iPnone5:i386; iPhone5s-iPhone7 Plus:x86_64
真机: iPhone3gs-iPhone4s:armv7; iPhone5-iPhone5c:armv7s; iPhone5s-iPhone7Plus:arm64

我们需要把对应的framework引入工程中即可使用, 但是我们平时可能用到真机调试,也可能使用模拟器调试,来回切换framework会很繁琐,所以一般使用终端把支持真机和模拟器的framework合并成一个framework(fat版)

命令格式:lipo -create 第一个.framewor下的esec文件的绝对路径 第二个.framework下的esec文件的绝对路径 -output 最终的.framework文件路径
操作如下:

 
Swift + framework 的制作(基于project)_第5张图片
lipocreate.gif

4.使用脚本编译

如上的单调频繁的操作可能会让你感觉累觉不爱,xcode目前支持的脚本编译,包治百病

1).创建Aggregate

targets点击左下角 “+”, 添加Aggregate

 
Swift + framework 的制作(基于project)_第6张图片
创建Aggregate.png

2). 添加脚本文件

 
Swift + framework 的制作(基于project)_第7张图片
添加脚本.png

Run Script 中可以放入待执行的脚本文件, 也可以放入待执行的脚本, 但是放入待执行脚本时, 遇到过文件权限的问题(涉及pod管理的framework), 所以此处推荐使用脚本文件的路径代替脚本

3).使用到的脚本如下:

#!/bin/sh
#要build的target名
TARGET_NAME=${PROJECT_NAME}
if [[ $1 ]]
then
TARGET_NAME=$1
fi
UNIVERSAL_OUTPUT_FOLDER="${SRCROOT}/${PROJECT_NAME}_Products/"

#创建输出目录,并删除之前的framework文件
mkdir -p "${UNIVERSAL_OUTPUT_FOLDER}"
rm -rf "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework"

#分别编译模拟器和真机的Framework
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

#拷贝framework到univer目录
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework" "${UNIVERSAL_OUTPUT_FOLDER}"

#合并framework,输出最终的framework到build目录
lipo -create -output "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework/${TARGET_NAME}"

#删除编译之后生成的无关的配置文件
dir_path="${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/"
for file in ls $dir_path
do
if [[ ${file} =~ ".xcconfig" ]]
then
rm -f "${dir_path}/${file}"
fi
done
#判断build文件夹是否存在,存在则删除
if [ -d "${SRCROOT}/build" ]
then
rm -rf "${SRCROOT}/build"
fi
rm -rf "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator" "${BUILD_DIR}/${CONFIGURATION}-iphoneos"
#打开合并后的文件夹
open "${UNIVERSAL_OUTPUT_FOLDER}"

4). 脚本以及生成的framework位置

 
Swift + framework 的制作(基于project)_第8张图片
脚本位置:framework位置.png

5).检测 framework支持的类型

使用lipo -info ...查看合成的framework支持的类型 包含:i386, Plus:x86_64, armv7, arm64

 
Swift + framework 的制作(基于project)_第9张图片
framework 支持框架.gif

6.framework制作过程中的坑

1. Module

在工程中, 我们可以通过桥接文件引入需要使用的OC或者C相关的, 但是swift动态库中, 是不能使用桥接文件的,比如: 需要使用CommonCrypto里面的内容, 在工程中可以直接导入CommonCrypto使用, 在库中则不能使用

 
8056F28B-E7F2-46C2-A2DF-D9429B6F25B1.png

这个问题可以使用模块化解决:
1).创建一个模块化文件, 并加入对应模块

 
Swift + framework 的制作(基于project)_第10张图片
26C785ABDF49722CC331A28B994DD3E9.png
module CCommonCrypto [system] {
header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/CommonCrypto/CommonCrypto.h"
export *
}

2). 模块化的使用
在需要使用的文件中 输入 import CCommonCrypto即可使用相关内容

3)参考文档
https://spin.atomicobject.com/2015/02/23/c-libraries-swift/ 英文原文
http://blog.csdn.net/quentingui/article/details/44115285 翻译
http://www.ethanwhy.com/2016/12/17/swift-framework-call-objective-c-c-language/
https://stackoverflow.com/questions/25248598/importing-commoncrypto-in-a-swift-framework/37125785#37125785
https://github.com/onmyway133/arcane


更新(2018-10-12):
xcode10中, swift增加了对 CommonCrypto的支持,在需要使用的 CommonCrypto的swift库中,不需要Module的方式,直接添加使用即可,如下:

import CommonCrypto

自己封装的库也可能使用了pod管理,基于workspace的库的制作将在接下来的文章中谈到...

以上仅是个人使用总结,欢迎批评指正补充~~~~~~~

你可能感兴趣的:(iOS技术汇,ios,swift,xcode)