补记说明:本文于 2026 年 10 月 25 日整理并首次发布。页面日期是这份经验的归档日期,历史案例与当前开放状态分别说明。
“两个包都发了。”这句话,最近我越来越不敢随口说。
咫尺是我正在做的一款双人互动 App。给它发更新时,我会从同一份代码分别打 iPhone 和 Mac 包。有一批,iPhone 已经可以给外部测试的人用,Mac 也上传成功、处理成有效构建了,却还没到外部测试者手里。
原因很具体:前一个 Mac 构建还在等待 Beta 审核。新包可以上传、可以进入内部测试,提交外部审核却被挡住了。发布记录里留下了一个报错:ANOTHER_BUILD_IN_REVIEW。
如果你也准备把 App 发给朋友试用,这篇可以帮你先分清:更新还卡在哪里,该向谁确认。
这篇接着十月初的上架笔记往后写。那篇解决账号和第一次签名,这篇讲有了持续迭代以后,怎样分清“我做出了一个包”和“别人已经拿到了这个版本”。文中的 TestFlight 是 Apple 用来分发测试版的工具。咫尺仍在受邀内测,这些经历还不能当成已经走完 App Store 正式上架。
先看人能拿到哪一版,再看我上传了哪一版#
我翻到的 10 月 24 日发布记录里,iPhone 的新包已经进入外部测试;Mac 的新包虽然有效,也进入了内部组,但旧构建还在等审核。我选择保留旧审核,没有为了提交新包就让它过期。
因此,对外说版本时,至少要分开记:
| 进到哪一步 | 这一步实际证明了什么 |
|---|---|
| 本机归档成功 | 有一个归档产物,接下来还要导出和核验 |
| 签名、导出通过 | 形成了对应渠道的安装或上传包 |
| 上传成功 | Apple 收到了这次提交 |
| Apple 处理通过 | 构建可进入后续测试或审核流程 |
| 内部组可测试 | 这个内部组的人可以取得构建 |
| 外部组可测试 | 这个外部组的人可以取得构建 |
Apple 的说明也把上传后的处理单独列出:上传之后还要处理,构建才会出现在 App Store Connect;构建号用来区分同一 App 的不同构建。Apple:上传构建
外部测试还要看组和审核状态。同一版本同时只能有一个构建在等待审核;是否通知测试者,也有自己的步骤。我的那次报错,正好卡在这里。Apple:邀请外部测试者
现在我更愿意写“iPhone 外部测试可用,Mac 新包仅内部可用”。多写几个字,下一次就不用猜谁拿到了哪一版。
App 加了小组件和手表,签名也跟着多了几份#
最初,我容易把签名理解成给一个 App 盖章。后来咫尺加了小组件、通知扩展、分享扩展和 Apple Watch,才发现安装包里面还有许多要分别核对的目标。
这次手表接入就碰到了:工程已经能编译,Apple 那边的 App ID 和 App Group 关联却没有随之自动就绪。账号持有人补好对应关联之后,签名才过。
我因此把检查放到了产物上:主 App 和扩展的版本是否一致,身份是不是从同一份配置推出来的,应该嵌入的扩展有没有进去,签出来的权限和描述文件是否对应。Mac 包不该带上的 iPhone 分享扩展,也要检查确实没有带进去。
咫尺的 Mac 版是原生 SwiftUI macOS App,复用部分代码,沿用既有的应用身份。它仍有自己的沙盒和权限配置。主 App 能签,并不能替里面每个扩展回答这些问题。
给别人复用这段经验,我会建议先画一张很小的表:每个目标叫什么、给哪个平台、嵌在哪里、需要什么能力。遇到签名失败,沿着表找到出问题的那一项,比不断重试整个归档更有方向。
有的警告不挡上传,却影响以后怎么查崩溃#
音视频依赖又让我认识了另一类问题:缺 dSYM。
dSYM 保存调试符号,可以帮助把崩溃里的地址还原成更容易定位的代码位置;它必须和那次构建的二进制对应。随便找一个同名文件放进去没有用。Apple:查找缺失的调试符号
咫尺用到的两个音视频原生库,上传时出现过缺符号警告。iPhone 的构建流程后来补了同版本源码构建、UUID 匹配和符号检查;Mac 对应的符号还没补齐,发布记录里仍留着警告。Mac 包当时能上传,但这些库的崩溃定位能力有缺口。
我想保留的经验是:遇到警告,先弄清它影响什么,再决定如何处理。能上传、能安装、出问题后能定位,应该分别核对。不能因为当下没挡住发布,就把警告从记录里抹掉。
Mac 发给别人,还有不同的路#
咫尺这次通过 TestFlight 给 Mac 测试者发包,所以检查的是商店分发所需的 App、安装包签名和平台记录。
如果选择在商店外直接分发 Mac 软件,还会遇到 Developer ID、Apple 公证和 Gatekeeper 下的启动检查。它是另一条交付路径,不能拿 TestFlight 上传成功来证明,也不能拿本机调试签名代替。Apple:在 Mac App Store 之外分发
因此,我现在会在写打包脚本之前,先写清这一次要到哪里:本机验证、内部测试、外部测试,还是正式分发。脚本的退出条件跟着这个目标走。
下一次发版,我会留下这几项#
源码版本、平台、展示版本与构建号、导出产物、Apple 处理状态、测试组状态,再加一条实际安装反馈。iPhone 和 Mac 分开记;有后端变化,就把对应服务版本也记下来。
我会把其中最常用的部分写成一张小回执。下面只是自编的格式示例,没有填入真实账号或测试组信息:
| 要回答的问题 | 示例写法 |
|---|---|
| 这次给谁 | iPhone 的受邀测试者 |
| 他们现在能拿到什么 | 当前外部组中可取得的测试构建 |
| 新包到了哪一步 | Mac 新包已处理,外部分发仍需确认 |
| 上一版是否仍然可用 | 保留上一可用构建,单独记有效状态 |
| 下一步是谁做什么 | 核对审核结果,再确认测试组可取得的版本 |
完整源码、签名材料和渠道身份可以在私有发布记录里对应,对外解释通常不需要把它们全部贴出来。最值得让测试者看到的是平台、可用状态和接下来该怎样更新。
收到“我这边还没有更新”时,也可以按这个顺序排查:先确认他用的哪一端,再确认加入的是不是这次分发范围,最后才回头看上传记录。把用户所在的那条路找对,比对着一条“上传成功”反复重试更有效。
还有一种情况要提前说清:同一平台同时有内部测试者和外部测试者,两个人看到不同版本也可能符合当时的分发状态。版本提示应当面向这个人真正能取得的包,避免让他来回刷新一个尚未向自己开放的版本。
尤其是 App 里的更新提示,应该指向目标用户真的能取得的版本。上传了一份更新的包,不代表现在就该让所有人去下载它。
这些记录不能替代真机测试,但能让我在收到“怎么还没更新”的时候,先看清卡在哪一步,再决定下一步做什么。
本文整理于 2026 年 10 月 25 日,案例来自此前的发布记录,没有在写作时重新打包、提交审核或核验当前审核队列。签名和分发结果也不能代表音视频、系统通知等功能已经逐项通过真机验收。
产品当前的安装范围见咫尺公开介绍。这里发布的是经验文章,不会触发 App 打包、签名或渠道审核。