怎么确认一个 .ipa 可以提交?上传前 12 项本地自检清单
一句话定义:IPA 上传前自检,指在把已签名的 .ipa 送往 App Store Connect 之前,于本地对包结构、元数据、签名与合规声明做一次静态核验,用来把「上传后才被 Apple 拒绝」的问题提前暴露;它不修改产物,也不依赖 macOS。
上传工具只负责把文件送进 Apple 的交付通道,报错信息往往只有一行 ITMS 编号,而服务端处理还要排队等待。把可静态判断的问题提前挡掉,能明显减少无效上传和来回返工。下面这份清单按「包结构 → Info.plist → 签名 → 合规」四组展开,共 12 项。
一、先把三个环节分开
| 环节 | 做什么 | 典型失败 | 本地能否提前发现 |
|---|---|---|---|
| 构建签名 | 生成 .ipa,写入签名与描述文件 |
用错证书、entitlements 不匹配 | 能(静态检查) |
| 上传传输 | 把 .ipa 送到交付通道 |
鉴权失败、网络中断 | 部分(凭证与连通性) |
| 服务端处理 | Apple 校验二进制、生成构建版本 | ITMS-90022、隐私清单警告 | 大部分能(对照本文清单) |
本文只覆盖「构建签名已完成之后的静态核验」,不涉及上传工具的选择与配置。
二、12 项自检清单
A. 包结构(第 1–4 项)
Payload/下只有一个.app目录,且名称与产物预期一致。Payload/*.app内没有嵌套的.app,Frameworks、PlugIns等目录层级正确;多余目录常触发ITMS-90205/90206。- 包内不含误打入的
__MACOSX、.DS_Store、*.p12、.mobileprovision等敏感或无关文件。 - 图标资源齐全,包含 1024×1024 的 App Store 图标与 Asset Catalog 要求的各档尺寸(
ITMS-90704/90022/90023)。
B. Info.plist(第 5–8 项)
CFBundleIdentifier与 App Store Connect 中创建 App 时填写的 Bundle ID 完全一致,大小写敏感。CFBundleVersion(build 号)未与此前上传过的任何版本重复;同号包会被静默丢弃,界面上却显示「上传成功」。CFBundleShortVersionString使用规范的版本号格式,且不低于已上架版本。MinimumOSVersion、设备族、启动页配置、以及相机/定位/相册等权限的描述字符串齐全且与真实功能匹配。
C. 签名与描述文件(第 9–10 项)
- 签名证书是 Distribution(发布证书) 而非 Development(
ITMS-90189的常见成因)。 - entitlements 中的
application-identifier、team-identifier、推送环境aps-environment、关联域名等,与embedded.mobileprovision中的授权一致。
D. 合规声明(第 11–12 项)
- 使用了「必要理由 API」的 SDK 是否随包提供
PrivacyInfo.xcprivacy隐私清单,且声明内容与真实数据流一致。自 2024 年起 Apple 对这类 API 有强制要求,缺失或声明不实会收到ITMS-91053一类警告。 - 加密合规材料齐备:Export Compliance 相关问题的答案可在 App Store Connect 中回答;含自研或第三方加密实现时提前准备说明。
三、跨平台怎么查(Windows / Linux / macOS 都能跑)
.ipa 本质是 zip,先列出结构:
unzip -l app.ipa | head -40
读取 Info.plist 不需要 macOS,用 Python 标准库即可:
import zipfile, plistlib
with zipfile.ZipFile('app.ipa') as z:
name = next(n for n in z.namelist()
if n.startswith('Payload/') and n.endswith('.app/Info.plist'))
info = plistlib.loads(z.read(name))
for k in ('CFBundleIdentifier', 'CFBundleVersion',
'CFBundleShortVersionString', 'MinimumOSVersion'):
print(k, '=', info.get(k))
macOS 上可额外做签名侧校验:
unzip -p app.ipa 'Payload/*.app/Info.plist' | plutil -p -
codesign -dv --verbose=4 Payload/xxx.app
security cms -D -i Payload/xxx.app/embedded.mobileprovision
Windows 或云主机没有 codesign 时,仍可完成第 1–8、11–12 项;签名有效性建议交给构建侧(macOS CI)验证,上传机只承担静态自检。
四、现象 → 自检项对照表
| 现象 | 优先核对 |
|---|---|
| 上传成功但后台看不到构建版本 | 第 6 项 build 号、第 5 项 Bundle ID |
ITMS-90022 / 90023 |
第 4 项图标尺寸 |
ITMS-90205 / 90206 |
第 2 项包内目录结构 |
ITMS-90189 |
第 9 项发布证书 |
ITMS-91053 / 隐私清单警告 |
第 11 项隐私清单 |
| 上传提示鉴权失败 | 不在产物内,查 App 专用密码或 API Key |
五、推荐执行顺序
- 记录本次 build 号与上一版本 build 号,确认已递增;
- 解包列出
Payload结构,确认无多余目录与误打入文件; - 读取
Info.plist的关键字段,与 App Store Connect 后台逐项比对; - 核对签名证书类型与 entitlements;
- 检查隐私清单与加密合规材料;
- 把本次校验输出与 build 号绑定归档,便于 CI 留痕与回溯。
六、常见问答
Q:Windows 上能做完整的签名校验吗?
A:包结构和元数据校验完全可行(zip + plistlib),但 codesign 依赖 macOS。合理分工是把签名有效性放在构建侧(macOS CI),Windows / 云主机只做上传前的静态自检。
Q:本地自检能替代上传后的 ITMS 回执吗?
A:不能。Apple 服务端还会做二进制扫描、符号与 API 使用检查。自检的价值是把可预见的高频问题提前挡掉,减少无效上传与排队等待。
Q:每次都要手工跑吗?
A:可以脚本化。把上面的 Python 片段接进 CI,作为上传步骤前的门禁,输出结构化结果并与 build 号一起归档;发现缺项时直接阻断上传。
本文要点
.ipa是 zip,绝大多数上传前问题可用静态检查发现,不强依赖 macOS。- 最高频的两项是 build 号重复与 Bundle ID 不一致,二者常表现为「上传成功却看不到构建版本」。
- 包结构、Info.plist、签名、合规四类共 12 项,按本文顺序核对最省时间。
- 自检不能替代 ITMS 回执,但能显著减少无效上传;脚本化后可直接作为 CI 门禁。
浙公网安备 33010602011771号