首页 文章

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

提问于
浏览
96

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

enter image description here

因为:

  • 模拟器仍然使用 32-bit

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

我们必须使胖库在设备和模拟器上运行项目 . 即支持Frameworks中的32位和64位 .

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

以下是我重现的步骤:

  • Build Settings 中设置 ONLY_ACTIVE_ARCH=NO

enter image description here

  • 将支持 armv7 armv7s arm64 i386 x86_64 添加到 Architectures (当然)

enter image description here

  • Build Framework并在Finder中打开它:

enter image description here

enter image description here

  • 将此框架添加到另一个项目中

实际结果:

但最终我仍然遇到在设备和模拟器上同时使用此框架运行项目的问题 .

  • 如果我从 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 issues有关,但并不重复 .


更新:

我找到了"dirty hack"这个案子 . 看我的answer below . 如果有人知道更方便的方式 - 请告诉我!

6 回答

  • 75

    @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 来创建一个胖框架,而不是Realm的 strip-frameworks.sh 在提交到App Store时删除不必要的切片 .

  • 54

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

    • 创建一个可可触摸框架 .

    • 将bitcode设置为否 .

    • 选择目标并选择编辑方案 . 选择Run并从Info选项中选择Release .

    • 无需其他设置 .

    • 现在为任何模拟器构建框架,因为模拟器在x86架构上运行 .

    • 单击Project Navigator中的Products组,找到.framework文件 .

    • 右键单击它并单击在查找器中显示 . 复制并粘贴到任何文件夹中,我个人更喜欢名称'simulator' .

    • 现在为Generic iOS Device构建框架并按照步骤6到9进行操作 . 只需将文件夹重命名为'device'而不是'simulator' .

    • 复制设备.framework文件并粘贴到任何其他目录中 . 我更喜欢两者的直接超级目录 . 所以目录结构现在变成:

    • 桌面

    • 设备

    • MyFramework.framework

    • 模拟器

    • MyFramework.framework

    • MyFramework.framework现在打开终端并cd到桌面 . 现在开始输入以下命令:

    lipo -create'device / MyFramework.framework / MyFramework''模拟器/ MyFramework.framework / MyFramework'-output'MyFramework.framework / MyFramework'

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

  • 9

    我的答案涵盖以下几点:

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

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

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

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

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

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

    第3步:现在选择你的框架目标,并在“Build Phases”下选择“Add Run Script”并复制下面的脚本代码)

    Step4:最后,再次构建,您的框架已经为模拟器和设备兼容性做好了准备 . 欢呼!!!!

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

    参见参考图片:

    enter image description here

    enter image description here

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

    这不是那么明确的解决方案,但只有办法,我发现:

    • Build Settings 中设置 ONLY_ACTIVE_ARCH=NO

    • 为模拟器构建库

    • 为设备构建库

    • 在您的框架的控制台 Products 文件夹中打开(您可以打开它从那里打开框架文件夹和 cd ..

    enter image description here

    enter image description here

    • Products 文件夹运行 this 脚本 . 它在此文件夹中创建fat Framework . (或按照以下 3. 4. 中的说明手动完成)

    要么:

    • 通过此脚本使用 lipo 组合这两个框架(将 YourFrameworkName 替换为您的框架名称)
    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
    
    • 用新的二进制替换现有框架之一:
    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
    

    • 利润: ./YourFrameworkName.framework - is ready-to-use fat binary! 您可以将其导入您的项目!

    For project, that not in Workspaces:

    您也可以尝试使用this gist,如here所述 . 但似乎它不适用于工作空间中的项目 .

  • 0

    我只是想通过@odm更新this great answer . 从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
    

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

  • 1

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

    TLDR;

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

    接下来是更长的答案


    我在这个主题上做了类似的研究(答案底部的链接) .

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

    这是Apple Developer Forums主题Exporting app with embedded framework的长引用(带有一点标记):

    从框架项目中导出框架的正确方法是什么?目前唯一的方法就是你所做的:为模拟器和iOS设备构建目标 . 导航到该项目的Xcode的DerivedData文件夹,并将两个二进制文件合并为一个单独的框架 . 但是,在Xcode中构建框架目标时,请确保将目标设置“仅构建活动体系结构”调整为“否” . 这将允许Xcode为多种binarty类型(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 uses lipoRealm uses lipo .


    IMPORTANT DETAIL

    有雷达:Xcode 6.1.1 & 6.2: iOS frameworks containing simulator slices can't be submitted to the App Store并在Realm#1163Carthage#188上进行了长时间的讨论,结束于特殊的解决方法:

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

    迦太基有特殊代码:CopyFrameworks和相应的文件:

    此脚本适用于由通用二进制文件触发的App Store提交错误 .

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

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

    还有好文章:Stripping Unwanted Architectures From Dynamic Libraries In Xcode .

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


    我建议阅读的主题链接,因为它包含此问题的另一个方面:代码签名 - Creating iOS/OSX Frameworks: is it necessary to codesign them before distributing to other developers?

相关问题