使用Xcode 8创建一个通用框架?

8
我的公司的iOS框架旨在在实际的iOS设备上运行。这个框架目前生成为Xcode项目中的一个附加目标,该项目还会生成一个应用程序。(这使得调试框架相对容易。)
最近我们收到了要求,希望它也可以在模拟器上运行。我现在可以让它做到,并且下一步是创建一个编译版本,可以在真实设备和模拟器上都能运行。可惜的是,我没有找到任何关于使用Xcode 8完成这个任务的材料。有些材料解释如何在旧版本的Xcode中完成,但某个版本的Xcode中有效的方法可能在后来的版本中不起作用或不被建议使用(我们已经有一种创建通用框架的方法失效了)。我尝试使用一些Xcode 8之前的脚本,但它们都无法使用。
是否有人成功使用Xcode 8生成了iOS的通用框架?如果有的话,如何做呢?
提前感谢大家提供的任何帮助。
Aaron Adelman

这是我写的一份完整指南,涉及此主题:https://eladnava.com/publish-a-universal-binary-ios-framework-in-swift-using-cocoapods/ - Elad Nava
4个回答

17

我可以做到这一点,因为我目前正在使用Xcode 8开发iOS、watchOS和tvOS的通用框架。

我的方法是创建一个聚合目标(跨平台),并在其构建阶段添加一个运行脚本。该脚本基本上编译了iphonesimulator和iphoneos的iOS目标。

之后,它会创建一个新的二进制文件,将两个文件合并(lipo -create -output)。

您能否发布用于生成通用框架的当前构建脚本,以便我可以指导您哪里出了错?请考虑到问题可能不在脚本本身,而在于设置有效架构、架构或者您如何签署目标等方面。我建议您暂时不要在目标的通用设置中勾选自动管理签名选项,并手动设置您的配置文件和证书。

运行脚本:

    #!/bin/sh

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}/iOS"

# Step 1. Build Device and Simulator versions on iOS
xcodebuild -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${PROJECT_NAME}"  -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' clean build
xcodebuild -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${PROJECT_NAME}" -sdk iphoneos 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}/iOS"

# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/iOS/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"

# Step 4. Convenience step to copy the framework to the project's directory
mkdir -p "${TMPDIR}/${PROJECT_NAME}/Frameworks/iOS"

cp -R "${UNIVERSAL_OUTPUTFOLDER}/iOS/${PROJECT_NAME}.framework" "${TMPDIR}/${PROJECT_NAME}/Frameworks/iOS"


# Step 6. Create .tar.gz file for posting on the binary repository
cd "${TMPDIR}"

# We nest the framework inside a Frameworks folder so that it unarchives correctly
tar -zcf "${PROJECT_NAME}.framework.tar.gz" "${PROJECT_NAME}/Frameworks/"
mv "${PROJECT_NAME}.framework.tar.gz" "${PROJECT_DIR}/"

# Step 7. Convenience step to open the project's directory in Finder
#open "${PROJECT_DIR}"

需要注意的是,在构建设置中我将“仅构建活动架构”设置为NO,同时将有效架构设置为arm64、x86_64、i386、armv7和armv7s。架构为${ARCHS_STANDARD} armv7s。

我还设置了一个用户定义的构建设置BITCODE_GENERATION_MODE bitcode。通过该构建设置,我确保生成启用了bitcode的二进制文件。


这将需要在代码中导入多个框架,当处理可视化设计时,这将变得非常麻烦。我正在尝试设置它们共享相同的模块名称。 - Knight0fDragon
1
@niczm25 我不确定是否必要,但我总是对我的框架进行代码签名。 - iOSAddicted
@iOSAddicted 对我来说,在实际设备上这个代码可以正常运行,但是在模拟器上编译时,尝试使用框架类在消费示例项目中无法编译。我的框架和消费客户端应用程序都是用Swift 3编写的。在真实的iOS设备上一切都很正常,所以我不确定是否可能是构建设置的架构问题。我完全按照这篇文章中的步骤进行操作。在模拟器上,我的框架导入成功,但是无法找到框架中的任何内容。不确定我错过了什么。 - zic10
1
这是一个伟大而优雅的构建脚本,比我以前见过的所有脚本都好得多。 只有一些小建议。 这假定工作区与项目同名;你可以明确地指定,并且可以通过自定义构建变量进行覆盖(使用 WORKSPACE_NAME := "${PROJECT_NAME}.xcworkspace")。 类似地,对于库名称:- LIBRARY_NAME := ${PROJECT_NAME}。 - Gordon Dove
你会如何使用BITCODE_GENERATION_MODE呢? 我目前正在尝试创建未启用位码的iphonesimulator二进制文件和启用位码的iphoneos二进制文件。 - hariszaman
显示剩余17条评论

3

构建iOS/tvOS通用/全包框架

https://github.com/unchartedworks/universalbuild

使用方法:

universalbuild (-p|--project 名称.xcodeproj) (-s|--scheme 方案名称) (-c|--configuration 配置名称)

示例:

git clone https://github.com/cruisediary/Pastel.git
cd Pastel
universalbuild -p ./Pastel.xcodeproj -s Pastel -c Debug

看起来很有趣,但是除非它能与工作区一起使用,否则我不能给你一个+1 :( - Gordon Dove
很遗憾,它不是那样工作的。在我实施之前,我进行了一些研究,许多框架项目使用项目而不是工作区。这就是为什么它只支持Xcode项目的原因。 - webcpu

1
运行脚本以创建通用框架(无递归问题)
#!/bin/sh

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

# Next, work out if we're in SIM or DEVICE
if [ "false" == ${ALREADYINVOKED:-false} ]
then

export ALREADYINVOKED="true"

if [ ${PLATFORM_NAME} = "iphonesimulator" ]
then
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
else
xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
fi

# 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 "${PROJECT_DIR}"

fi

0

我也曾经遇到过同样的问题。我有一系列作为源pod使用的cocoapods,但需要将它们转换为二进制pods。在大量搜索、试错和破解之间,我想出了一个脚本,取得了巨大的成功。它基于你在这里看到的脚本,但重构成函数和一些调试输出(当某些东西丢失时,它会进入/tmp日志)。

该脚本可以在github gist中找到:https://gist.github.com/intere/bc380fa45ccf23976d3fc297522d29a8

顺便说一下,我一直在使用Xcode 9(9.2,然后是9.3,现在是9.4)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接