App Store 上架全流程:一个保守派手写党的“被迫数字化”之旅

Prompt修理师
2025-12-19 13:57
阅读 784

大家好,我是老李,坐标成都,一个坚持手写每一行代码的“老古董”程序员。白天在一家中小厂折腾云原生和 K8s,晚上兼职给朋友团队搞搞 iOS 项目——说是兼职,其实更多时候是被产品经理半夜微信轰炸:“老李,App 能不能下周上架?我们投资人要看数据了!”

上周五晚上 10 点,我一边喝着茶(成都人嘛,加班也得养生),一边盯着 Xcode 构建失败的日志发呆,突然意识到:我居然还没系统整理过 App Store 上架的完整流程。虽然不是天天干这活,但每次都要翻一堆文档、Stack Overflow、Apple Developer 官方指南,甚至还得求教隔壁组那个 95 后实习生(他居然用 AI 自动生成了 Info.plist 模板……我表示震惊但不说)。

于是,趁着周末躺平刷《黑神话》的间隙,我决定把这次“实战经验”写下来。不为别的,就为了下次再被催上线时,能快速翻出这篇博客,而不是在凌晨三点对着“Invalid Bundle ID”报错想砸电脑。


为什么上个 App 这么难?

很多人以为 iOS 上架就是点个“Archive”然后上传就完事了——天真!Apple 的审核机制就像川菜里的花椒,表面温和,咬一口麻到灵魂。尤其是现在 Apple 对隐私、权限、元数据的要求越来越严,稍不注意就会被拒。

去年双11期间,我们一个电商类 App 就因为没在 NSPhotoLibraryUsageDescription 里写清楚“为什么需要访问相册”,直接被拒三次。测试同事还吐槽:“你这文案写得像运维写的 Shell 脚本,冷冰冰的。” 好吧,我承认,我确实写了:“用于上传头像”——结果 Apple 要的是“用户可通过此功能上传个人头像以个性化账户界面”。

你看,连文案都得有 UX 思维。这哪是开发?这是全栈+产品+法务+翻译!


我的“手工流水线”:从代码到 App Store

作为一个抗拒低代码、AI 辅助(虽然最近也在偷偷试 GitHub Copilot)的老派开发者,我的上架流程依然坚持“手写为主,工具为辅”。下面是我梳理的实战路径,结合了 Swift/SwiftUI 最佳实践和几次踩坑教训。

第一步:项目配置——别让 Bundle ID 成为你的心魔

Bundle ID 是整个流程的起点。我见过太多团队在这里翻车:开发用 com.company.dev.app,测试用 com.company.test.app,上线又想用 com.company.app,结果证书对不上,Xcode 直接罢工。

我的做法

  • 开发阶段用 com.company.app.dev
  • TestFlight 内测用 com.company.app.beta
  • 正式版只用 com.company.app

这样通过 Xcode 的 Configurations(Debug / Release / Beta)配合 .xcconfig 文件管理,干净利落。比如:

// Configurations/AppStore.xcconfig
PRODUCT_BUNDLE_IDENTIFIER = com.company.app

顺便吐槽一句:Xcode 的自动管理签名(Automatically manage signing)虽然方便,但在 CI/CD 环境下简直是灾难。我宁愿手动配证书 + Provisioning Profile,至少心里有底。

第二步:代码层面的合规检查

Apple 现在对隐私权限要求极其严格。哪怕你代码里只是 import Photos,但没在 Info.plist 里声明用途,直接拒审。

我在项目里加了一个 PrivacyChecker.swift,作为构建前的检查脚本(虽然有点偏执,但有效):

// PrivacyChecker.swift - 仅用于本地验证,非运行时
// 如果引入了 Contacts,必须声明 NSContactsUsageDescription
#if canImport(Contacts)
#warning("请确认 Info.plist 中已添加 NSContactsUsageDescription")
#endif

另外,SwiftUI 项目要特别注意 App Lifecycle 的合规性。比如后台任务、位置更新等,必须使用 BGTaskSchedulerCLLocationManager 的正确回调,否则会被判定为“滥用后台资源”。

第三步:构建与归档 —— Archive 不等于成功

很多人以为 Product > Archive 成功就万事大吉。错!真正的考验在 ValidationUpload 阶段。

我常用的命令行工具组合(虽然我爱手写,但重复操作交给工具更靠谱):

# 使用 xcodebuild 手动归档(可集成到 CI)
xcodebuild archive \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -archivePath ./build/MyApp.xcarchive

# 导出 IPA(用于 TestFlight 或 Ad Hoc)
xcodebuild -exportArchive \
  -archivePath ./build/MyApp.xcarchive \
  -exportPath ./build/ipa \
  -exportOptionsPlist ExportOptions.plist

其中 ExportOptions.plist 是关键,它决定了你是上传到 App Store 还是分发给测试。我的模板如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>app-store</string> <!-- 或 testflight -->
    <key>teamID</key>
    <string>YOUR_TEAM_ID</string>
    <key>uploadBitcode</key>
    <false/>
    <key>compileBitcode</key>
    <false/>
</dict>
</plist>

注:Bitcode 我一般关掉。除非你做 Watch App 或特殊优化,否则开 Bitcode 只会拖慢构建速度,还可能引发符号表错乱。

第四步:上传到 App Store Connect —— 别信 Transporter 的 UI

Apple 自家的 Transporter 应用看似友好,实则玄学。我有次上传成功,App Store Connect 却显示“Processing…”三天不动。最后发现是 metadata 里的截图分辨率不符合要求(iPhone 14 Pro Max 必须用 1290x2796,少一像素都不行)。

现在我更倾向用 命令行工具 altool(虽然已被 deprecated)或新的 notarytool + xcrun 组合。不过最稳的还是直接用 Xcode Organizer 上传——前提是网络稳定(成都电信晚高峰你懂的)。

第五步:填写元数据 —— 这是产品经理的战场,也是开发的噩梦

App Store Connect 后台填信息比写递归还痛苦。你需要:

  • App 名称、副标题、关键词(总共 100 字符,精打细算)
  • 截图(每种设备尺寸都要,还得符合 Human Interface Guidelines)
  • 隐私标签(Apple 强制要求的数据收集清单)
  • 审核备注(建议写清楚测试账号、特殊功能入口)

有一次我们 App 因为有个“隐藏彩蛋页面”没在审核备注里说明,审核员找不到,直接拒了。后来我学会了在备注里写:

“测试账号:test@company.com / 123456。彩蛋入口:在设置页连续点击 Logo 5 次。”

——是不是很像给 QA 写测试用例?


工具链对比:手写党 vs 现代化流水线

虽然我坚持手写核心逻辑,但在上架这种重复性工作上,我也开始接受一些“现代化工具”。以下是我试用过的几款,做个简评:

工具 优点 缺点 我的使用场景
Xcode 自带流程 无缝集成,适合小团队 手动操作多,易出错 个人项目、MVP 快速验证
Fastlane 自动化强,社区插件多 学习成本高,Ruby 依赖 团队项目,CI/CD 集成
GitHub Actions + xcodebuild 免费,版本可控 配置复杂,调试困难 开源项目、远程构建
Transporter App 图形化,上传直观 黑盒操作,错误提示模糊 偶尔应急

目前我的主力是 Fastlane + 手动校验。比如用 fastlane deliver 管理元数据,但截图和隐私标签仍手动检查——毕竟 AI 还看不懂设计规范。


审核被拒怎么办?心态要稳

根据我的经验,80% 的拒审原因集中在以下几点:

  1. 隐私描述缺失或模糊
  2. App 崩溃或功能不可用(尤其新 iOS 版本)
  3. 图标/截图不符合规范(比如带状态栏、有竞品 Logo)
  4. 使用了私有 API(即使只是误报)

应对策略:

  • 收到拒审邮件后,先看 Resolution Center 的具体截图和日志
  • 复现问题时,务必用 最新版 TestFlight + 最新 iOS beta
  • 回复审核团队时,语气要礼貌,附上修复证据(视频/GIF 最佳)

有一次我们因为一个后台崩溃被拒,其实是 iOS 17 的 SceneDelegate 生命周期变更导致的。修完后我录了个 10 秒视频上传,当天就过了。


结语:手写代码的人,也要拥抱流程

说实话,写这篇博客的过程中,我重新审视了自己对“自动化”的偏见。手写代码是为了掌控逻辑,但上架流程的本质是标准化交付——这里不需要创造力,需要的是可靠性和一致性。

所以现在我的态度是:核心逻辑手写,重复流程交给工具。毕竟,省下的时间可以多喝两杯茶,或者陪娃搭乐高(成都的节奏,就得这么舒服)。

如果你也在被 App Store 折磨,希望这篇“保守派实战指南”能帮到你。如果有什么坑我没提到,欢迎留言——咱们程序员,不就是靠互相救火才活到今天的吗?

P.S. 刚收到消息,App 终于过审了。开心!今晚火锅走起 🌶️

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝