发布预编译的CocoaPods

时间:2014-04-16 18:38:48

标签: xcode cocoapods

我们目前正在为使用CocoaPods的客户构建SDK。

我们遇到的主要问题是我们的老板希望SDK成为黑盒子。他希望我们预编译代码以保护我们的源代码。

为了保护我们的代码,我们可以在Podspec中做些什么吗?

3 个答案:

答案 0 :(得分:18)

您可以通过创建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的第三方开发人员的设置应该 easy

我相信我将在下面概述的解决方案满足这些约束中的每一个。我会概述 如何从头开始构建.framework项目,以便您可以将这些步骤应用于现有项目 项目如果你愿意的话。我还将包含用于轻松创建的项目模板 .framework。

概述

  

查看示例项目,该项目显示sample/Serenity中执行这些步骤的结果   。目录

在项目中,我们将有三个目标:静态库,捆绑和聚合。

静态库目标将源构建到静态库(.a)中并指定哪些头 将是" public",这意味着我们在分发时可以从.framework访问它们。

捆绑目标将包含我们的所有资源,并且可以从框架中加载。

聚合目标将为i386 / armv6 / armv7 / armv7s构建静态库,生成胖框架 二进制,并且还构建了bundle。当您计划分发时,您将运行此目标 .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>

创建框架头文件后,您需要将其设为&#34; public&#34;头。上市 标题是将被复制到.framework的标题,可以由使用您的标题的人导入 框架。这不同于&#34;项目&#34;将与框架一起分发的标头。 这种区别使您可以拥有公共和私有API的概念。

更改文件[XCode 4.4+中的目标会员资格可见性] (Can't change target membership visibility in Xcode 4.5), 您需要选择您创建的静态库目标(Serenity),打开Build Phases选项卡:

Xcode 4.X: 单击Add Build Phase&gt;添加复制标题。

Xcode 5: 从菜单中添加Build Phases。点击编辑器&gt;添加构建阶段 - &gt;添加复制标题构建阶段。注意:如果菜单选项显示为灰色,则您需要单击构建阶段下方的空白区域以重新获得焦点并重试。

您将看到公共,私有和项目标题的3个部分。要修改任何标题的范围,请在节之间拖放标题文件。或者,您可以打开Project Navigator并选择标题。接下来,展开File Inspector的Utilities窗格。 (Cmd的+选项+ 0)。

查看&#34;目标会员资格&#34;分组并确保选中.h文件旁边的复选框。 从&#34; Project&#34;更改标题的范围公共&#34;公共&#34;。您可能必须取消选中并选中该框以获取下拉列表。这将确保标头得到 复制到复制标题阶段的正确位置。

第3步:更新公共标题位置

默认情况下,静态库项目会将私有和公共标头复制到同一文件夹: /usr/local/include。为避免错误地将私有标头复制到我们的框架,我们希望确保 我们的公共标题被复制到一个单独的目录,例如$(PROJECT_NAME)Headers。要更改此设置, 在Project Navigator中选择项目,然后单击&#34; Build Settings&#34;标签。搜索&#34;公开 报头&#34;然后设置&#34;公共标题文件夹路径&#34;到&#34; $(PROJECT_NAME)标题&#34;适用于所有配置。 如果您正在使用多个框架,请确保此文件夹是唯一的。

正在进行的步骤:向框架添加新来源

每当您向框架添加新源时,您必须决定是公开还是公开.h 不。要修改标题范围,您将按照与步骤2相同的过程进行操作。默认情况下,标题为&#39; s 范围将是&#34;项目&#34;,这意味着它不会被复制到框架的公共标题中。

步骤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:准备框架以用作依赖目标

为了使用静态库,好像它是一个框架,我们将生成基本的 静态库目标中框架的框架。为此,我们将包含一个简单的后期构建 脚本。通过在Project Navigator中选择项目,选择目标,然后选择,添加构建后脚本 &#34;构建阶段&#34;标签。

Xcode 4.X:点击添加构建阶段&gt;添加运行脚本

Xcode 5:选择编辑器菜单&gt;添加构建阶段&gt;添加运行脚本构建阶段

将以下脚本粘贴到运行脚本构建阶段的源部分。您可以通过单击重命名阶段 阶段的标题(我已将其命名为“#34;准备框架&#34;”)。

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平台。

当我们想要将框架分发给第三方开发人员时,这会发生变化。第三方 开发人员无法为每个平台重建框架,因此我们必须提供 什么被称为&#34;脂肪二元&#34;由可能的静态库组成的版本 平台。这些平台包括:i386,armv6,armv7和armv7s。

为了生成这个胖二进制文件,我们将为每个平台构建静态库目标。

第1步:创建聚合目标

点击文件&gt;新目标&gt; iOS&gt;其他并创建一个新的Aggregate目标。标题为&#34; Framework&#34;。

步骤2:将静态库添加为依赖目标

将静态库目标添加到&#34; Target Dependencies&#34;。

第3步:构建其他平台

要构建另一个平台,我们将使用&#34;运行脚本&#34;阶段执行一些基本命令。 添加一个新的&#34;运行脚本&#34;为您的聚合目标构建阶段并将以下代码粘贴到其中。

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,目标名称为 Serenity)然后你需要在那一行显式设置目标名称。例如:

SF_TARGET_NAME=Serenity

第4步:构建和验证

您现在已经设置了所有内容,以便为第三方开发人员构建可分发的.framework。尝试 建立总目标。完成后,展开Xcode中的Products文件夹,右键单击 静态库并单击&#34;在Finder&#34;中显示。如果这不打开Finder到静态库的位置 存在然后尝试打开 ~/Library/Developer/Xcode/DerivedData/<project name>/Build/Products/Debug-iphonesimulator/

在此文件夹中,您将看到.framework文件夹。

您现在可以将.framework拖到别处,压缩,上传,然后将其分发给您 第三方开发者。

答案 1 :(得分:4)

我成功地使用Podspec作为例子:

Pod::Spec.new do |s|
  s.name         = "EstimoteSDK"
  s.version      = "1.3.0"
  s.summary      = "iOS library for Estimote iBeacon devices"
  s.homepage     = "http://estimote.com"
  s.author       = { "Estimote, Inc" => "contact@estimote.com" }
  s.platform     = :ios 
  s.source       = { :git => "https://github.com/Estimote/iOS-SDK.git", :tag => "  {s.version}" }
  s.source_files =  'EstimoteSDK/Headers/*.h'
  s.preserve_paths = 'EstimoteSDK/libEstimoteSDK.a'
  s.vendored_libraries = 'EstimoteSDK/libEstimoteSDK.a'
  s.ios.deployment_target = '7.0'
  s.frameworks = 'UIKit', 'Foundation', 'SystemConfiguration', 'MobileCoreServices', 'CoreLocation'
  s.requires_arc = true
  s.xcconfig  =  { 'LIBRARY_SEARCH_PATHS' => '"$(PODS_ROOT)/EstimoteSDK"',
               'HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/Headers/EstimoteSDK"' }
  s.license      = {
    :type => 'Copyright',
    :text => <<-LICENSE
      Copyright 2013 Estimote, Inc. All rights reserved.
      LICENSE
  }
end

答案 2 :(得分:1)

Swift不支持

静态库,因此对于来这里寻找 Swift SDK 的解决方案的人来说,这是一个不错的{{3}解释它应该如何完成。

更新

Swift 4现在原生支持静态swift库。

  边注:      

对于仍然有兴趣创建快速动态库的人来说,这仍然是一篇很好的,非常有用的文章article