亲测有效!React Native iOS 打包缺少 Hermes dSYM 终极解决方案

390 阅读4分钟

开头:恼人的 dSYM 丢失问题

如果你在开发 React Native 应用,当你使用 Xcode 的 Product > Archive 功能打包应用并准备上传到 App Store Connect 或分发给测试人员时,你很可能会遇到下面这个熟悉的错误弹窗:

The archive did not include a dSYM for the hermes.framework... Ensure that the archive's dSYM folder includes a DWARF file for hermes.framework with the expected UUIDs.

image.png

简单来说,这个错误意味着你的应用归档文件(.xcarchive)中,缺少了 Hermes JS 引擎的调试符号文件(dSYM)。dSYM 文件对于符号化应用的崩溃报告至关重要,没有它,你在 Firebase Crashlytics 或 Sentry 等平台上看到的崩溃日志将是一堆无法解读的内存地址,这会让你定位和修复线上问题变得异常困难。

2. 排查:当网络上的通用方案都不奏效时

相关问题issues: github.com/facebook/re…

遇到问题,我们首先会求助于网络。通常,你会找到以下几种主流的解决方案:

  1. 修正 Build Phases 脚本:确保 "Bundle React Native code and images" 阶段的脚本是官方推荐的最新版本。
  2. 手动下载 dSYM:在 ios 目录下运行 ../node_modules/react-native/scripts/download-hermes-symbols.sh 脚本来手动获取 dSYM 文件。
  3. 使用 Post-action 脚本:在 Xcode Scheme 的 Archive 配置中,添加一个在归档后运行的脚本,手动调用 dsymutil 来生成 dSYM。
  4. 直接忽略:没错。如果你没有强迫症。这这是一个警告。你完全可以忽略它~

然而,在某些情况下,特别是对于特定版本的 React Native(如 0.74+)或存在环境问题的 Xcode 项目,你会发现以上所有方法可能都无法解决问题。这正是我们接下来要解决的问题。

3. 插曲:神秘的 Archive Post-actions 失效之谜

在排查过程中,我们曾尝试过一个看起来最优雅的方案:使用 Xcode 的 Scheme > Archive > Post-actions 功能。理论上,这个功能允许我们在 Archive 成功之后执行一个自定义脚本,这正是生成 dSYM 的完美时机。

我们配置了脚本,甚至为了诊断它是否执行,使用了包含 exit 1(强制失败)的测试代码。然而,诡异的事情发生了:

无论如何配置,Archive 过程总是成功,脚本从未被触发!

这证明,在某些 Xcode 项目环境中,Post-actions 功能会因为缓存、配置损坏或 Xcode 本身的 Bug 而彻底失效。继续在这条路上探索是行不通的。因此,我们必须放弃这个方案,转向一个更可靠、更底层的实现方式。

4. 最终解决方式:注入到核心构建阶段 (Build Phases)

既然 Post-actions 不可靠,我们就将脚本直接注入到 App Target 的核心构建流程中,并利用 Xcode 提供的原生选项,让它只在我们需要的时候(Archive 期间)运行。

这是目前最稳定、最可靠的解决方案。

第一步:导航至 Build Phases

  1. 在 Xcode 左侧导航栏点击你的项目根目录。
  2. 在中间的编辑区选择你的主应用 Target (例如 taroDemo)。
  3. 点击顶部的 Build Phases 标签页。

第二步:添加并配置新的运行脚本阶段

  1. 点击 + 号按钮,选择 New Run Script Phase
  2. 一个新的 "Run Script" 阶段会出现在列表底部。用鼠标将它拖拽到整个列表的最末尾,确保它在所有编译和资源拷贝步骤之后执行。

第三步:编写脚本并设置关键选项

  1. 展开这个新的 "Run Script" 阶段。
  2. 勾选关键选项:将 "For install builds only" 复选框勾选上。这个选项的作用是告诉 Xcode,此脚本仅在 Archive 操作时运行,而在普通的编译或调试运行时则跳过,完美满足我们的需求。
  3. 粘贴最终脚本:将下面的脚本代码完整地粘贴到脚本输入框中。
# ==============================================================================
#  Final Script: Using available environment variables to generate Hermes dSYM
#  This script runs only for Archive because "For install builds only" is checked.
# ==============================================================================

echo "✅ Running final dSYM generation script for Archive..."

# ------------------------------------------------------------------------------
# 1. 定义 Hermes.framework 的源文件路径
#    在 Archive 过程中, 它位于最终打包的 .app 文件内部的 Frameworks 目录中。
#    我们可以使用 $TARGET_BUILD_DIR 和 $CONTENTS_FOLDER_PATH 来精确定位它。
# ------------------------------------------------------------------------------
HERMES_FRAMEWORK_PATH="$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/Frameworks/hermes.framework"

# ------------------------------------------------------------------------------
# 2. 定义 dSYM 的输出路径
#    我们将生成的 dSYM 文件放置在 $BUILT_PRODUCTS_DIR 中。
#    Xcode 在打包 .xcarchive 时, 会自动从这个目录收集所有的 .dSYM 文件。
# ------------------------------------------------------------------------------
DSYM_OUTPUT_PATH="$BUILT_PRODUCTS_DIR/hermes.framework.dSYM"

echo "ℹ️ Source Hermes Path: $HERMES_FRAMEWORK_PATH"
echo "ℹ️ Destination dSYM Path: $DSYM_OUTPUT_PATH"

# ------------------------------------------------------------------------------
# 3. 检查路径是否存在并执行 dsymutil
# ------------------------------------------------------------------------------
if [[ -d "$HERMES_FRAMEWORK_PATH" ]]; then
  echo "📍 Hermes framework found. Proceeding with dSYM generation..."
  
  # 调用 dsymutil 工具生成 dSYM 文件
  dsymutil "$HERMES_FRAMEWORK_PATH/hermes" -o "$DSYM_OUTPUT_PATH"
  
  echo "🎉 Success! Hermes dSYM generated successfully."
else
  echo "❌ ERROR: Hermes framework NOT FOUND at the expected path."
  echo "Path checked: $HERMES_FRAMEWORK_PATH"
  echo "Please check if the framework is correctly embedded in your app."
  exit 1
fi

第四步:验证

现在,重新执行 Product > Archive。构建过程应该会顺利成功。之后,你可以在 Report Navigator (日志导航器) 中查看此次 Archive 的构建日志,在日志中搜索 🎉 Success!,你应该能看到脚本成功执行的打印信息。

image.png

image.png

至此,你生成的 .xcarchive 包已经完整包含了 Hermes 的 dSYM,可以放心上传和分发了。再也不会弹出这个恼人的弹窗警告啦!

好了,今天的分享就到这里,下次见。