ios 如何导出“胖”Cocoa Touch Framework(用于模拟器和设备)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29634466/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 05:37:33  来源:igfitidea点击:

How to export "fat" Cocoa Touch Framework (for Simulator and Device)?

iosxcodecocoa-touchframeworksios-universal-framework

提问by skywinder

With Xcode 6we get ability to create own Dynamic Cocoa Frameworks.

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

enter image description here

在此处输入图片说明

Because of:

因为:

  • Simulator still use 32-bitlibrary

  • beginning June 1, 2015 app updates submitted to the App Store must include 64-bit support and be built with the iOS 8 SDK (developer.apple.com)

  • 模拟器仍然使用32-bit

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

We have to make fat library to run project on devices and simulators. i.e. support both 32 and 64 bit in Frameworks.

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

But I didn't find any manuals, how to exportuniversal fat Framework for future integration with other projects (and share this library with someone).

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

Here is my steps to reproduce:

这是我重现的步骤:

  1. Set ONLY_ACTIVE_ARCH=NOin the Build Settings

    enter image description here

  2. Add support armv7 armv7s arm64 i386 x86_64to Architectures(for sure)

  1. 设置ONLY_ACTIVE_ARCH=NOBuild Settings

    在此处输入图片说明

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

enter image description here

在此处输入图片说明

  1. Build Framework and open it in Finder:
  1. 构建框架并在 Finder 中打开它:

enter image description hereenter image description here

在此处输入图片说明在此处输入图片说明

  1. Add this framework to another project
  1. 将此框架添加到另一个项目

Actual result:

实际结果:

But in the end I still have problem with running project with this framework on devices and simulator at once.

但最终我仍然无法同时在设备和模拟器上使用此框架运行项目。

  • if I take framework from Debug-iphoneosfolder - it works on devices and gets error on simulators: ld: symbol(s) not found for architecture i386

    xcrun lipo -info CoreActionSheetPicker
    

    Architectures in the fat file: CoreActionSheetPicker are: armv7 armv7s arm64

  • if I take framework from Debug-iphonesimulatorfolder - it works on simulators. and I have error on device: ld: symbol(s) not found for architecture arm64

    xcrun lipo -info CoreActionSheetPicker
    

    Architectures in the fat file: CoreActionSheetPicker are: i386 x86_64

  • 如果我从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

So, how to create a dynamic framework that works on devices and simulators?

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

This answer related to Xcode 6 iOS Creating a Cocoa Touch Framework - Architectures issuesbut it's not duplicate.

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



Update:

更新:

I found a "dirty hack" for this case. See my answer below. If someone knows more convenient way - please, let me know!

对于这种情况,我发现了一个“肮脏的黑客”。请参阅下面的我的回答。如果有人知道更方便的方法 - 请告诉我!

采纳答案by Stanislav Pankevich

The actuality of this answer is: July 2015. It is most likely that things will change.

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

TLDR;

TLDR;

Currently Xcode does not have tools for automatic export of universal fat framework so developer must resort to manual usage of lipotool. Also according to this radarbefore submission to AppStore developer who is framework's consumer also must use lipoto strip off simulator slices from a framework.

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

Longer answer follows

更长的答案如下



I did similar research in the topic (the link at the bottom of the answer).

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

I had not found any official documentation about distribution of so my research was based on exploration of Apple Developer Forums, Carthage and Realm projects and my own experiments with xcodebuild, lipo, codesigntools.

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

Here's long quote (with a bit of markup from me) from Apple Developer Forums thread Exporting app with embedded framework:

这是来自 Apple Developer Forums 线程导出应用程序与嵌入式框架的长引用(带有我的一些标记):

What is the proper way to export a framework from a framework project?

Currently the only way is exactly what you have done:

  • Build the target for both simulator and iOS device.
  • Navigate to Xcode's DerivedData folder for that project and lipo the two binaries together into one single framework. However, when you build the framework target in Xcode, make sure to adjust the target setting 'Build Active Architecture Only' to 'NO'. This will allow Xcode to build the target for multiple binarty types (arm64, armv7, etc). This would be why it works from Xcode but not as a standalone binary.

  • Also you'll want to make sure the scheme is set to a Release build and build the framework target against release. If you are still getting a library not loaded error, check the code slices in the framework.

  • Use lipo -info MyFramworkBinaryand examine the result.

lipo -info MyFrameworkBinary

Result is i386 x86_64 armv7 arm64

  • Modern universal frameworks will include 4 slices, but could include more: i386 x86_64 armv7 arm64If you don't see at least this 4 it coud be because of the Build Active Architecture setting.

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

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

  • 为模拟器和 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 设置。

This describes process pretty much the same as @skywinder did it in his answer.

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

This is how Carthage uses lipoand Realm uses lipo.

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



IMPORTANT DETAIL

重要细节

There is radar: Xcode 6.1.1 & 6.2: iOS frameworks containing simulator slices can't be submitted to the App Storeand a long discussion around it on Realm#1163and Carthage#188which ended in special workaround:

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

before submission to AppStore iOS framework binaries must be stripped off back from simulator slices

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

Carthage has special code: CopyFrameworksand corresponding piece of documentation:

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

This script works around an App Store submission bugtriggered by universal binaries.

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

Realm has special script: strip-frameworks.shand corresponding piece of documentation:

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

This step is required to work around an App Store submission bugwhen archiving universal binaries.

在归档通用二进制文件时,需要此步骤来解决App Store 提交错误

Also there is good article: Stripping Unwanted Architectures From Dynamic Libraries In Xcode.

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

I myself used Realm's strip-frameworks.shwhich worked for me perfectly without any modifications though of course anyone is free to write a one from scratch.

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



The link to my topic which I recommend to read because it contains another aspect of this question: code signing - Creating iOS/OSX Frameworks: is it necessary to codesign them before distributing to other developers?

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

回答by skywinder

This is not so clear solution, but there is only way, that I find:

这不是那么清楚的解决方案,但我发现只有一种方法:

  1. Set ONLY_ACTIVE_ARCH=NOin the Build Settings

    • Build library for simulator
    • Build library for device
  2. Open in console Productsfolder for your framework (you can open it by open framework folder and cd ..from there)

  1. 设置ONLY_ACTIVE_ARCH=NOBuild Settings

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

enter image description hereenter image description here

在此处输入图片说明在此处输入图片说明

  1. Run thisscript from Productsfolder. It creates fat Framework in this folder. (or do it manually as explained below in 3. 4.)
  1. 从文件夹运行脚本Products。它在此文件夹中创建胖框架。(或按照下面3. 4. 中的说明手动执行)


Or:

或者:

  1. Combine these 2 Frameworks using lipoby this script (replace YourFrameworkNameto your Framework name)

    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
    
  2. Replace with new binary one of the existing frameworks:

    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
    
  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. Profit: ./YourFrameworkName.framework- is ready-to-use fat binary!You can import it to your project!
  1. 利润:./YourFrameworkName.framework-是现成的胖二进制!您可以将其导入到您的项目中!


For project, that not in Workspaces:

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

You can also try to use this gistas described here. But it seems, that it not works for projects in workspaces.

您也可以尝试使用此处所述的要点。但似乎它不适用于工作区中的项目。

回答by odm

@Stainlav answer was very helpful but what I did instead was to compile two versions of the framework (one for the device and one for simulator) and then added the following Run Script Phaseto automatically copy the precompiled framework required for the running architecture

@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

This way I don't have use lipoto create a fat framework neither the Realm's strip-frameworks.shto remove the unnecessary slices when submitting to the App Store.

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

回答by Nrip

basically for this i found very good solution. you just need to follow these simple steps.

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

  1. Create a cocoa touch framework.
  2. Set bitcode enabled to No.
  3. Select your target and choose edit schemes. Select Run and choose Release from Info tab.
  4. No other setting required.
  5. Now build the framework for any simulator as simulator runs on x86 architecture.
  6. Click on Products group in Project Navigator and find the .framework file.
  7. Right click on it and click on Show in finder. Copy and paste it in any folder, I personally prefer the name 'simulator'.
  8. Now build the framework for Generic iOS Device and follow the steps 6 through 9. Just rename the folder to 'device' instead of 'simulator'.
  9. Copy the device .framework file and paste in any other directory. I prefer the immediate super directory of both. So the directory structure now becomes:
    • Desktop
    • device
      • MyFramework.framework
    • simulator
      • MyFramework.framework
    • MyFramework.framework Now open terminal and cd to the Desktop. Now start typing the following command:
  1. 创建一个可可触摸框架。
  2. 将启用的位码设置为否。
  3. 选择您的目标并选择编辑方案。选择运行并从信息选项卡中选择发布。
  4. 无需其他设置。
  5. 现在为任何模拟器构建框架,因为模拟器在 x86 架构上运行。
  6. 单击 Project Navigator 中的 Products 组并找到 .framework 文件。
  7. 右键单击它,然后单击在查找器中显示。将其复制并粘贴到任何文件夹中,我个人更喜欢名称“模拟器”。
  8. 现在为 Generic iOS Device 构建框架并按照步骤 6 到 9 操作。只需将文件夹重命名为“device”而不是“simulator”。
  9. 复制设备 .framework 文件并粘贴到任何其他目录中。我更喜欢两者的直接超级目录。所以目录结构现在变成:
    • 桌面
    • 设备
      • MyFramework.framework
    • 模拟器
      • MyFramework.framework
    • MyFramework.framework 现在打开终端并 cd 到桌面。现在开始输入以下命令:

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

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

and that's it. Here we merge the simulator and device version of MyFramework binary present inside MyFramework.framework. We get a universal framework that builds for all the architectures including simulator and device.

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

回答by Dorian Roy

I just want to update this great answerby @odm. Since Xcode 10, the CURRENT_ARCHvariable doesn't reflect the build architecture anymore. So I changed the script to check the platform instead:

我只想通过@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

I also added a line to clear the target directory before copying, because I noticed that additional files in subdirectories wouldn't be overwritten otherwise.

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

回答by Sandip Patel - SM

My Answer cover the below points:

我的回答涵盖以下几点:

  • Make framework which works for both simulator and Device

  • How to export “fat” Cocoa Touch Framework (for Simulator and Device both)?

  • Undefined symbols for architecture x86_64

  • ld: symbol(s) not found for architecture x86_64

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

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

  • 体系结构 x86_64 的未定义符号

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

Steps 1: First build your frameworks with Simulator target

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

Steps 2: After success of simulator building process, now build for your framework with device target selection or Generic iOS Device selection

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

Step 3: Now select your framework target and for that Under "Build Phases" select "Add Run Script" and copy the below script code)

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

Step4: Now finally, build again and your framework is ready for both simulator and device compatibility. Hurray!!!!

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

[Note: We must have both compatible framework ready before final step4 (simulator and device architecture compatible, if not please follow above steps 1 and 2 correctly)

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

See the reference image:

查看参考图片:

enter image description here

在此处输入图片说明

enter image description here

在此处输入图片说明

Put below code into shell area:

将以下代码放入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"