108

使用Xcode 6,我们可以创建自己的 Dynamic Cocoa Frameworks

在此处输入图像描述

因为:

  • 模拟器仍然使用32-bit

  • 从 2015 年 6 月 1 日开始,提交到 App Store 的应用更新必须包含 64 位支持并使用 iOS 8 SDK ( developer.apple.com )构建

我们必须制作胖库才能在设备和模拟器上运行项目。即在框架中支持 32 位和 64 位。

但是我没有找到任何手册,如何导出通用胖框架以供将来与其他项目集成(并与某人共享此库)。

这是我的重现步骤:

  1. 设置ONLY_ACTIVE_ARCH=NOBuild Settings

    在此处输入图像描述

  2. 添加支持armv7 armv7s arm64 i386 x86_64Architectures肯定)

在此处输入图像描述

  1. 构建框架并在 Finder 中打开它:

在此处输入图像描述 在此处输入图像描述

  1. 将此框架添加到另一个项目

实际结果:

但最后我仍然无法在设备和模拟器上同时运行这个框架的项目。

  • 如果我从Debug-iphoneos文件夹中获取框架 - 它适用于设备并在模拟器上出错:ld: symbol(s) not found for architecture i386

      xcrun lipo -info CoreActionSheetPicker
    

    胖文件中的架构:CoreActionSheetPicker 是:armv7 armv7s arm64

  • 如果我从Debug-iphonesimulator文件夹中获取框架 - 它适用于模拟器。我在设备上有错误:ld: symbol(s) not found for architecture arm64

      xcrun lipo -info CoreActionSheetPicker
    

    胖文件中的架构:CoreActionSheetPicker 是:i386 x86_64

那么,如何创建一个适用于设备和模拟器的动态框架呢?

这个答案与Xcode 6 iOS Creating a Cocoa Touch Framework - Architectures 问题有关,但不是重复的。


更新:

我为这种情况找到了一个“肮脏的黑客”。请参阅下面的答案。如果有人知道更方便的方法 - 请告诉我!

4

6 回答 6

82

这个答案的实际情况是:2015 年 7 月。情况很可能会发生变化。

TLDR;

目前 Xcode 没有自动导出通用胖框架的工具,因此开发人员必须求助于手动使用lipo工具。同样根据此雷达,在提交给作为框架消费者的 AppStore 开发人员之前,还必须使用lipo从框架中剥离模拟器切片。

更长的答案如下


我对该主题进行了类似的研究(答案底部的链接)。

我没有找到任何关于分发的官方文档,所以我的研究是基于对 Apple Developer Forums、Carthage 和 Realm 项目的探索以及我自己对xcodebuild, lipo,codesign工具的实验。

这是来自 Apple Developer Forums 线程Exporting app with embedded framework的长引用(带有我的一些标记):

从框架项目中导出框架的正确方法是什么?

目前唯一的方法就是你所做的:

  • 为模拟器和 iOS 设备构建目标。
  • 导航到该项目的 Xcode 的 DerivedData 文件夹,然后将两个二进制文件合并到一个框架中。但是,当您在 Xcode 中构建框架目标时,请确保将目标设置“仅构建活动架构”调整为“否”。这将允许 Xcode 为多种二进制类型(arm64、armv7 等)构建目标。这就是为什么它可以在 Xcode 中工作,但不能作为独立的二进制文件。

  • 此外,您还需要确保将方案设置为发布版本并针对发布构建框架目标。如果您仍然收到库未加载错误,请检查框架中的代码片。

  • 使用lipo -info MyFramworkBinary并检查结果。

lipo -info MyFrameworkBinary

结果是i386 x86_64 armv7 arm64

  • 现代通用框架将包括 4 个切片,但可能包括更多:i386 x86_64 armv7 arm64 如果您至少看不到这 4 个,可能是因为 Build Active Architecture 设置。

这描述的过程与@skywinder 在他的回答中所做的几乎相同。

这就是Carthage 使用 lipoRealm 使用 lipo的方式。


重要细节

有雷达:Xcode 6.1.1 和 6.2:包含模拟器切片的 iOS 框架无法提交到 App Store ,并且在Realm#1163Carthage#188上围绕它进行了长时间的讨论,最终以特殊的解决方法结束:

在提交到 AppStore iOS 框架二进制文件之前,必须从模拟器切片中剥离出来

Carthage 有特殊代码:CopyFrameworks和相应的文档:

此脚本可解决由通用二进制文件触发的App Store 提交错误。

Realm 有特殊的脚本:strip-frameworks.sh和相应的文档:

在归档通用二进制文件时,此步骤是解决App Store 提交错误所必需的。

还有一篇好文章:从 Xcode 中的动态库中剥离不需要的架构

我自己使用了 Realm strip-frameworks.sh,它在没有任何修改的情况下完美地为我工作,当然任何人都可以自由地从头开始编写一个。


我建议阅读我的主题的链接,因为它包含这个问题的另一个方面:代码签名 -创建 iOS/OSX 框架:是否有必要在分发给其他开发人员之前对它们进行代码签名?

于 2015-07-07T13:48:50.043 回答
59

这不是很明确的解决方案,但我发现只有一种方法:

  1. 设置ONLY_ACTIVE_ARCH=NOBuild Settings

    • 为模拟器构建库
    • 为设备构建库
  2. 在您的框架的控制台Products文件夹中 打开(您可以通过打开框架文件夹并cd ..从那里打开它)

在此处输入图像描述 在此处输入图像描述

  1. 从文件夹运行脚本。Products它在此文件夹中创建胖框架。(或者按照下面3. 4中的说明手动进行。 )

或者:

  1. 通过此脚本使用lipo组合这两个框架(替换YourFrameworkName为您的框架名称)

    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
    
  2. 替换为现有框架之一的新二进制文件:

    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
    

  1. 利润:./YourFrameworkName.framework-是现成的胖二进制文件!您可以将其导入您的项目!

对于项目,不在工作区中:

您也可以尝试使用这里描述的这个要点。但似乎它不适用于工作区中的项目。

于 2015-04-15T12:34:17.673 回答
10

@Stainlav 的回答非常有帮助,但我所做的是编译框架的两个版本(一个用于设备,一个用于模拟器),然后添加以下内容Run Script Phase以自动复制运行架构所需的预编译框架

echo "Copying frameworks for architecture: $CURRENT_ARCH"
if [ "${CURRENT_ARCH}" = "x86_64" ] || [ "${CURRENT_ARCH}" = "i386" ]; then
  cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
  cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

这样我就不用lipo创建一个胖框架,也不需要 Realmstrip-frameworks.sh在提交到 App Store 时删除不必要的切片。

于 2016-04-20T16:16:24.970 回答
2

基本上为此我找到了很好的解决方案。你只需要按照这些简单的步骤。

  1. 创建一个可可触摸框架。
  2. 将启用的位码设置为否。
  3. 选择您的目标并选择编辑方案。选择运行并从信息选项卡中选择发布。
  4. 无需其他设置。
  5. 现在为任何模拟器构建框架,因为模拟器在 x86 架构上运行。
  6. 单击 Project Navigator 中的 Products 组并找到 .framework 文件。
  7. 右键单击它,然后单击在查找器中显示。将其复制并粘贴到任何文件夹中,我个人更喜欢名称“模拟器”。
  8. 现在为通用 iOS 设备构建框架并按照步骤 6 到 9 进行操作。只需将文件夹重命名为“设备”而不是“模拟器”。
  9. 复制设备 .framework 文件并粘贴到任何其他目录中。我更喜欢两者的直接超级目录。所以目录结构现在变成了:
    • 桌面
    • 设备
      • MyFramework.framework
    • 模拟器
      • MyFramework.framework
    • MyFramework.framework 现在打开终端并 cd 到桌面。现在开始输入以下命令:

lipo -create 'device/MyFramework.framework/MyFramework' 'simulator/MyFramework.framework/MyFramework' -output 'MyFramework.framework/MyFramework'

就是这样。在这里,我们合并了 MyFramework.framework 中存在的 MyFramework 二进制文件的模拟器和设备版本。我们得到了一个通用框架,可以为包括模拟器和设备在内的所有架构构建。

于 2017-01-31T10:02:01.297 回答
2

我只想通过@odm更新这个很棒的答案。从 Xcode 10 开始,该CURRENT_ARCH变量不再反映构建架构。所以我改变了脚本来检查平台:

echo "Copying frameworks for platform: $PLATFORM_NAME"
rm -R "${SRCROOT}/Frameworks/Active"
if [ "${PLATFORM_NAME}" = "iphonesimulator" ]; then
    cp -af "${SRCROOT}/Frameworks/Simulator/." "${SRCROOT}/Frameworks/Active"
else
    cp -af "${SRCROOT}/Frameworks/Device/." "${SRCROOT}/Frameworks/Active"
fi

我还在复制之前添加了一行来清除目标目录,因为我注意到子目录中的其他文件不会被覆盖。

于 2018-11-05T17:11:01.257 回答
2

我的回答涵盖以下几点:

  • 制作适用于模拟器和设备的框架

  • 如何导出“胖”Cocoa Touch 框架(适用于模拟器和设备)?

  • 架构 x86_64 的未定义符号

  • ld:未找到架构 x86_64 的符号

第 1 步:首先使用 Simulator 目标构建您的框架

第 2 步:模拟器构建过程成功后,现在使用设备目标选择或通用 iOS 设备选择为您的框架构建

第 3 步:现在选择您的框架目标,然后在“构建阶段”下选择“添加运行脚本”并复制以下脚本代码)

第 4 步:现在最后,再次构建,您的框架已准备好与模拟器和设备兼容。欢呼!!!!

[注意:我们必须在最后一步之前准备好两个兼容的框架(模拟器和设备架构兼容,如果不兼容请正确执行上述步骤1和2)

请参阅参考图像:

在此处输入图像描述

在此处输入图像描述

将以下代码放入 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 "${BUILD_DIR}/${CONFIGURATION}-universal"

于 2017-03-08T11:26:22.073 回答