我们目前正在使用CocoaPods为客户构建一个SDK。
我们面临的主要问题是老板希望SDK成为黑匣子。他希望我们预先编译代码以保护我们的源代码。
在Podspec中我们可以做些什么来保护我们的代码?
最佳答案
您可以通过创建Static Framework
并将其包含在podspec的spec.vendored_frameworks
属性中来实现此目的。
http://guides.cocoapods.org/syntax/podspec.html#vendored_frameworks
请遵循以下教程,了解如何创建自己的静态框架。
https://github.com/jverkoey/iOS-Framework#walkthrough
如何为iOS创建静态框架
构建.framework时,我们需要满足一些约束条件:
开发框架时快速迭代构建。我们可能有一个简单的应用程序
.framework作为依赖项,我们希望快速迭代.framework的开发。
.framework的发行版本很少。
资源分配应该直观而不会使应用程序肿。
使用.framework为第三方开发人员进行设置应该很容易。
我相信,我将在下面概述的解决方案可以满足所有这些限制。我会概述
如何从头开始构建.framework项目,以便可以将这些步骤应用于现有的
如果您愿意的话。我还将包括用于轻松创建项目模板的项目模板。
.framework。
总览
查看一个示例项目,该示例项目显示sample/Serenity
中遵循这些步骤的结果
目录。
在项目中,我们将有三个目标:一个静态库,一个捆绑包和一个集合。
静态库目标会将源构建到静态库(.a)中并指定哪些标头
将是“公共”的,这意味着当我们分发.framework时,它们将是可访问的。
捆绑包目标将包含我们所有的资源,并且可以从框架中加载。
聚合目标将为i386 / armv6 / armv7 / armv7s构建静态库,生成胖框架
二进制文件,并同时构建捆绑包。计划分发该目标时,将运行此目标。
.framework。
当您使用框架时,您可能会有一个内部应用程序链接到
框架。该应用程序将照常链接到静态库目标并复制
复制资源阶段中的.bundle。这样的好处是仅构建框架代码
针对您正在积极使用的平台,从而大大缩短了构建时间。我们将做一个
框架项目中的少量工作,以确保您可以在应用程序中使用框架
第三方开发人员的方式相同(即导入应该可以
符合预期)。跳转至从属项目演练。
创建静态库目标
第1步:创建一个新的“ Cocoa Touch静态库”项目
产品名称将是您的框架的名称。例如,Serenity
将生成Serenity.framework
一旦我们设置了项目。
步骤2:创建主框架标题
开发人员希望能够通过导入<Serenity/Serenity.h>
来导入您的框架
标头。确保您的项目具有这样的标题(如果您创建了一个新的静态库,那么那里
应该已经是一个Serenity.h和Serenity.m文件;您可以删除.m)。
在此标头中,您将导入框架的所有公共标头。对于
例如,假设我们有一些带有.h和.m的Widget
。我们的Serenity.h文件看起来
像这样:
#import <Foundation/Foundation.h>
#import <Serenity/Widget.h>
创建框架头文件后,需要将其设置为“公共”头文件。上市
标头是将被复制到.framework的标头,那些使用
框架。这与不会随框架一起分发的“项目”标头不同。
这种区别使您可以拥有公共和私有API的概念。
更改文件的[XCode 4.4+中的目标成员身份可见性]
(Can't change target membership visibility in Xcode 4.5),
您需要选择创建的静态库目标(安全性),打开“构建阶段”选项卡:
Xcode 4.X:
单击添加构建阶段>添加副本标题。
Xcode 5:
从菜单中添加构建阶段。单击编辑器>添加构建阶段->添加副本标题构建阶段。注意:如果菜单选项显示为灰色,则需要单击“构建阶段”下面的空白以重新获得焦点并重试。
您将看到3个关于Public,Private和Project标头的部分。要修改任何标题的范围,请在各节之间拖放标题文件。或者,您可以打开项目浏览器并选择标题。接下来,展开“文件”检查器的“实用程序”窗格。
(Cmd + Option + 0)。
查看“目标成员身份”组,并确保选中.h文件旁边的复选框。
将标题的范围从“项目”更改为“公共”。您可能需要取消选中并选中复选框以获取下拉列表。这将确保标题
复制到复制标题阶段中的正确位置。
步骤3:更新公共标题位置
默认情况下,静态库项目会将私有和公共标头复制到同一文件夹:
/usr/local/include
。为了避免将私有标头错误地复制到我们的框架中,我们要确保将我们的公共标头复制到单独的目录,例如
$(PROJECT_NAME)Headers
。要更改此设置,在项目浏览器中选择项目,然后单击“构建设置”选项卡。搜索“公共
标头”,然后将所有配置的“公共标头文件夹路径”设置为“ $(PROJECT_NAME)标头”。
如果使用多个框架,请确保该文件夹是唯一的。
正在进行的步骤:向框架添加新资源
每当您向框架添加新的源代码时,都必须决定是否公开公开.h或
不。要修改标头的范围,您将按照与步骤2相同的过程进行操作。默认情况下,标头的范围
作用域将是“项目”,这意味着它不会被复制到框架的公共头文件中。
步骤4:禁用代码剥离
我们不想从库中剥离任何代码。我们将其留给应用程序
链接到框架。要禁用代码剥离,我们必须修改以下配置
设置:
"Dead Code Stripping" => No (for all settings)
"Strip Debug Symbols During Copy" => No (for all settings)
"Strip Style" => Non-Global Symbols (for all settings)
步骤5:准备框架以用作从属目标
为了像使用框架一样使用静态库,我们将生成基本的
静态库目标中框架的骨架。为此,我们将包括一个简单的后构建
脚本。通过在“项目浏览器”中选择您的项目,选择目标,然后在
“构建阶段”标签。
Xcode 4.X:单击添加构建阶段>添加运行脚本
Xcode 5:选择编辑器菜单>添加构建阶段>添加运行脚本构建阶段
将以下脚本粘贴到运行脚本构建阶段的源代码部分。您可以通过以下方式重命名阶段:
阶段的标题(例如,我将其命名为“ Prepare Framework”)。
prepare_framework.sh
set -e
mkdir -p "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/A/Headers"
# Link the "Current" version to "A"
/bin/ln -sfh A "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/Current"
/bin/ln -sfh Versions/Current/Headers "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Headers"
/bin/ln -sfh "Versions/Current/${PRODUCT_NAME}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
# The -a ensures that the headers maintain the source modification date so that we don't constantly
# cause propagating rebuilds of files that import these headers.
/bin/cp -a "${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/A/Headers"
这将生成以下文件夹结构:
-- Note: "->" denotes a symbolic link --
Serenity.framework/
Headers/ -> Versions/Current/Headers
Serenity -> Versions/Current/Serenity
Versions/
A/
Headers/
Serenity.h
Widget.h
Current -> A
立即尝试构建您的项目,然后查看构建产品目录(通常
~/Library/Developer/Xcode/DerivedData/<ProjectName>-<gibberish>/Build/Products/...
)。你应该查看一个
libSerenity.a
静态库,一个Headers
文件夹和一个Serenity.framework
文件夹,包含框架的基本框架。
创建框架分发目标
在积极开发框架时,我们只关心构建要测试的平台。对于
例如,如果我们正在iPhone模拟器上进行测试,则只需要构建i386平台即可。
当我们希望将框架分发给第三方开发人员时,情况会发生变化。第三方
开发人员无法选择为每个平台重建框架,因此我们必须提供
静态库的所谓“胖二进制”版本,由可能的
平台。这些平台包括:i386,armv6,armv7和armv7s。
为了生成这个胖二进制文件,我们将为每个平台构建静态库目标。
第1步:创建汇总目标
单击文件>新建目标> iOS>其他,然后创建一个新的聚合目标。给它命名为“框架”。
步骤2:将静态库添加为从属目标
将静态库目标添加到“目标依赖项”。
步骤3:建立其他平台
为了构建另一个平台,我们将使用“运行脚本”阶段来执行一些基本命令。
将一个新的“运行脚本”构建阶段添加到聚合目标,并将以下代码粘贴到该目标中。
build_framework.sh
set -e
set +u
# Avoid recursively calling this script.
if [[ $SF_MASTER_SCRIPT_RUNNING ]]
then
exit 0
fi
set -u
export SF_MASTER_SCRIPT_RUNNING=1
SF_TARGET_NAME=${PROJECT_NAME}
SF_EXECUTABLE_PATH="lib${SF_TARGET_NAME}.a"
SF_WRAPPER_NAME="${SF_TARGET_NAME}.framework"
# The following conditionals come from
# https://github.com/kstenerud/iOS-Universal-Framework
if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]
then
SF_SDK_PLATFORM=${BASH_REMATCH[1]}
else
echo "Could not find platform name from SDK_NAME: $SDK_NAME"
exit 1
fi
if [[ "$SDK_NAME" =~ ([0-9]+.*$) ]]
then
SF_SDK_VERSION=${BASH_REMATCH[1]}
else
echo "Could not find sdk version from SDK_NAME: $SDK_NAME"
exit 1
fi
if [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]
then
SF_OTHER_PLATFORM=iphonesimulator
else
SF_OTHER_PLATFORM=iphoneos
fi
if [[ "$BUILT_PRODUCTS_DIR" =~ (.*)$SF_SDK_PLATFORM$ ]]
then
SF_OTHER_BUILT_PRODUCTS_DIR="${BASH_REMATCH[1]}${SF_OTHER_PLATFORM}"
else
echo "Could not find platform name from build products directory: $BUILT_PRODUCTS_DIR"
exit 1
fi
# Build the other platform.
xcrun xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk ${SF_OTHER_PLATFORM}${SF_SDK_VERSION} BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}" $ACTION
# Smash the two static libraries into one fat binary and store it in the .framework
xcrun lipo -create "${BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}" "${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}" -output "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}"
# Copy the binary to the other architecture folder to have a complete framework in both.
cp -a "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}" "${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}"
重要的提示
上面的脚本假设您的库名称与以下行中的项目名称匹配:
SF_TARGET_NAME=${PROJECT_NAME}
如果不是这种情况(例如,您的xcode项目名为SerenityFramework,而目标名称为
宁静),那么您需要在该行上显式设置目标名称。例如:
SF_TARGET_NAME=Serenity
步骤4:建立并验证
现在,您已完成一切工作,以构建可分发给第三方开发人员的.framework。尝试
建立总体目标。完成后,在Xcode中展开Products文件夹,右键单击
静态库,然后单击“在Finder中显示”。如果这样无法将Finder打开到静态库所在的位置
存在,然后尝试打开
~/Library/Developer/Xcode/DerivedData/<project name>/Build/Products/Debug-iphonesimulator/
。在此文件夹中,您将看到.framework文件夹。
现在,您可以将.framework拖到其他位置,将其压缩,上传并分发到您的
第三方开发商。
关于xcode - 发布预编译的CocoaPods,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23117574/