iOS 打包、证书与 TestFlight

iOS 发布的门槛不在 Flutter,而在 Apple 的签名体系:证书、App ID、描述文件三者必须匹配,错一环就报“签名不匹配”。好消息是这一套一旦配通,后面每次发版只是改版本号。本章按“准备环境 → 理解签名 → Xcode 配置 → 出包上传 → TestFlight 内测”的顺序讲清,最后给没有 Mac 的团队一条替代路径。

硬性前提

前置条件用途常见坑
macOS编译与签名只能在 Mac 上完成虚拟机不稳定,CI 用 macOS Runner
Xcode编译 iOS 工程,版本要匹配 Flutter 要求版本过旧时 flutter doctor 会提示
CocoaPods管理 iOS 原生依赖未安装时 pod install 失败,见下方排查
Apple Developer 账号发布必须付费账号(个人/公司 99 美元/年)免费账号只能真机调试,不能上架
flutter doctor -v                 # 先确认 Xcode 与 CocoaPods 两项都是绿勾
sudo gem install cocoapods        # 未安装时;也可用 brew install cocoapods
pod repo update                   # 依赖索引过旧时报找不到 pod 时执行

证书、App ID 与描述文件

概念作用谁来创建
Development 证书真机调试签名Xcode 自动管理即可
Distribution 证书上架与 TestFlight 签名Xcode 自动或手动在开发者后台生成
App ID与 Bundle Identifier 一一对应的唯一标识开发者后台自动注册
Provisioning Profile把证书、App ID、设备绑在一起的授权文件自动签名由 Xcode 维护

结论:团队规模不大时一律用 Xcode 的自动签名(Automatically manage signing),手动管理只在有多环境、多团队协作、需要精细控制时才值得。App Store Connect 里创建的 App 记录,其 Bundle ID 必须与工程里完全一致,且一旦提交首个版本就不能再改。

Xcode 配置

open ios/Runner.xcworkspace    # 必须打开 xcworkspace,打开 Runner.xcodeproj 会丢失 Pods 依赖

在 Xcode 里依次检查:

  • Runner target → GeneralIdentityBundle Identifier 用反向域名(如 com.example.shop),与 App Store Connect 记录一致。
  • Signing & Capabilities:勾选 Automatically manage signing,选择 Team,确认签名证书与描述文件没有红色报错。
  • Build SettingsVersioningVersion 对应 pubspec.yaml 的版本号前缀,Build 对应 + 后的构建号,Flutter 构建时会自动同步。
  • InfoApp Icons and Launch Images:用 flutter_launcher_iconsflutter_native_splash 生成,不要手改资源目录。

出包与上传

flutter build ipa --release                      # 产出 build/ios/ipa/*.ipa 与 .xcarchive
flutter build ipa --release --export-method app-store   # 明确导出用途
flutter build ipa --release --dart-define=ENV=prod      # 注入环境变量

flutter build ipa 内部会执行 pod install、编译并打包,产出的 .xcarchive 可直接用于上传。上传有三条路:

方式操作适用
XcodeProduct > ArchiveDistribute App本地调试签名问题最直观
Transporter拖入 .ipa,一键上传只需上传、不改配置
命令行xcrun altool / xcrun notarytool 系工具CI 自动化

上传后到 App Store Connect 的 TestFlight 页签等待处理完成,通常几分钟到半小时;首次上架还需要补全应用信息、隐私问卷与年龄分级。

TestFlight 内测流程

  • 内部测试:添加团队成员(最多 100 人),无需审核,构建处理完成后立即可测。
  • 外部测试:最多 10000 人,首个构建需要苹果审核(通常 1~2 天),适合公开灰度。
  • 导出合规:Info.plist 里配置 ITSAppUsesNonExemptEncryption 可免去每次上传都要回答加密问题。
  • 构建号必须递增:上传重复的 Build 号会被直接拒绝,CI 里用时间戳或自增号生成。
  • 测试反馈集中在 TestFlight 应用内的截屏与崩溃记录里,比微信群收集截图高效得多。

权限文案与隐私清单

权限文案写不好是最常见的审核退回原因,文案必须说明“用来做什么”,而不是“需要这个权限”。

<!-- ios/Runner/Info.plist -->
<key>NSCameraUsageDescription</key>
<string>用于拍摄商品照片并上传评价</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>用于从相册选择头像</string>
<key>NSUserTrackingUsageDescription</key>
<string>用于向你展示更相关的推荐内容(可拒绝)</string>

PrivacyInfo.xcprivacy 是新版要求的隐私清单文件,声明应用使用的 API 类别与数据采集类型(如 NSPrivacyAccessedAPICategoryUserDefaultsNSPrivacyCollectedDataTypes)。第三方 SDK 不提供该文件时会收到 ITMS-91053 警告,需在构建里补齐或替换该 SDK。App Store Connect 的“App 隐私”问卷必须与实际采集行为一致,收集了设备标识或位置就要如实勾选。

常见报错与排查

报错原因处理
CocoaPods not installed未装 CocoaPodssudo gem install cocoapods 后重跑
pod install 卡住或失败依赖索引过旧、网络受限pod repo update,或清理 Pods/Podfile.lock 重装
Signing for "Runner" requires a development team未选 TeamXcode 里选择 Team 并启用自动签名
No profiles for 'com.x.y' were foundBundle ID 与后台记录不一致核对标识符,或让 Xcode 重新生成描述文件
Provisioning profile doesn't include signing certificate证书与描述文件不匹配删除旧证书,重新自动签名
Invalid Bundle. ... does not support the minimum OS Version依赖库最低版本高于工程设置提高 IPHONEOS_DEPLOYMENT_TARGET
ITMS-91053: Missing API declaration缺隐私清单声明PrivacyInfo.xcprivacy 或升级相关 SDK

排查顺序固定:先在 Xcode 里确认签名区块无红色,再 flutter clean 后重新 pod install,最后才怀疑证书本身。

没有 Mac 怎么办

方案说明代价
云 Mac(MacinCloud 等)远程桌面操作 Xcode按小时计费,网络延迟影响体验
CI 云构建(Codemagic、GitHub Actions macOS Runner)在云端自动打包并上传 TestFlight需要把证书与密钥托管为 CI Secrets
外包打包把源码交给有 Mac 的团队出包源码安全与迭代速度都受影响

对没有 Mac 的团队,最实际的组合是:Android 本地发版,iOS 用 macOS Runner 自动构建 + TestFlight 分发,日常 iOS 调试借助同事的 Mac 或云 Mac 完成。

提交前自检

检查项要求
Bundle Identifier与 App Store Connect 记录完全一致,首版后不可更改
版本号与构建号pubspec.yamlversion: 1.2.0+45,构建号每次上传必须递增
图标与启动图无透明通道、无圆角(系统会自动裁切),用生成工具产出
权限文案每条权限都有说明用途的中文文案
隐私PrivacyInfo.xcprivacy 与 App 隐私问卷一致
账号与演示数据需登录的应用必须提供可用的测试账号
出口合规ITSAppUsesNonExemptEncryption 已配置

小结:iOS 发布前先确认 macOS + Xcode + CocoaPods 三件套,用 Xcode 自动签名维护证书、App ID 与描述文件,open ios/Runner.xcworkspace 里核对 Bundle Identifier 与 Team,然后 flutter build ipa 出包经 Xcode 或 Transporter 上传 TestFlight;权限文案与 PrivacyInfo.xcprivacy 要如实声明,遇到签名类报错按“Xcode 签名区块 → clean + pod install → 证书”三步排查,没有 Mac 就用 macOS CI Runner 代替。

笔记加载中…