35岁老码农的 App Store 上架血泪史:从简历焦虑到产品上线
凌晨五点半,天还没亮,我坐在书桌前,一边刷着 LeetCode 第 146 题(LRU Cache,真·面试高频题),一边盯着 Xcode 里那个卡在“Processing”状态三天的 App。咖啡杯已经凉了,但脑子里还在回放上周产品经理那句:“这个功能双11前必须上架,用户等着用呢!”
我是谁?一个 35 岁还在写代码的老程序员,早起型选手,每天 8 点准时坐到电脑前——不是因为自律,是因为娃 7:30 要上学,我得赶在他起床前把今天的刷题和晨会材料搞定。最近在准备跳槽,一边应付现有项目的 deadline,一边投简历、刷面试题挑战。说真的,现在大环境卷成麻花了,光会 CRUD 已经不够看了,K8s、云原生、CI/CD 流水线……哪个不得懂点?
但谁能想到,这次让我差点想砸 Mac 的,不是复杂的微服务架构,而是一个看起来人畜无害的 iOS App 上架流程。
起因:产品要上架,但没人干过这事
事情是这样的。我们团队去年接了个外包项目,客户是个传统企业,想做个内部员工打卡+通知的 App。技术栈很简单:Swift + SwiftUI,后端走 REST API,数据存在 AWS S3 + DynamoDB(毕竟咱云原生熟嘛)。开发过程还算顺利,测试也过了,结果到了发布阶段,发现整个团队——包括我这个“资深”——没人真正完整走过一次 App Store 上架流程。
PM 一脸无辜:“不是点个按钮就行了吗?”
运维兄弟冷笑:“iOS 上架?那玩意儿比部署 K8s Ingress 还玄学。”
测试小姐姐默默发来 Apple 审核被拒邮件截图:“Your app uses significant energy...(你的 App 耗电过高)”
那一刻,我看着自己刚更新的简历上写着“精通全栈开发、熟悉移动端发布流程”,心里咯噔一下:这要是跳槽面试被问“你怎么上架 App 的”,我怕是要当场表演一个简历自焚。
所以,这篇指南,既是给后来者的避坑手册,也是我自己的赎罪笔记。
准备阶段:资源、证书、配置文件,一个都不能少
App Store 上架的第一道门槛,不是代码,而是 Apple 生态里的各种“资源”——开发者账号、证书(Certificates)、标识符(Identifiers)、配置文件(Provisioning Profiles)。这些东西听起来像古代炼金术配方,但其实有逻辑可循。
1. 开发者账号(Apple Developer Program)
首先你得有个付费账号,99 美元/年。个人 or 公司?如果你是代表公司上架,必须用公司账号,而且需要 D-U-N-S 编码(邓白氏编码)。我们当时就卡在这儿——客户没申请过,等编码下来整整花了两周,直接导致上线延期。教训:提前搞!
💡 小贴士:如果是个人练手项目,用个人账号没问题;但涉及商业分发或企业内部分发,别省这点钱,老老实实用组织账号。
2. Bundle ID(应用唯一标识)
在 Apple Developer 后台 → Certificates, Identifiers & Profiles → Identifiers 里创建。
格式一般是 com.yourcompany.appname,比如 com.acmecorp.timesheet。一旦注册,无法修改!所以别手抖打错。我们有个实习生注册时写成 com.acmecorp.TimeSheet(大写 T),结果和 Xcode 里的小写不一致,打包时报错:
No profiles for 'com.acmecorp.TimeSheet' were found
查了半天才发现是大小写问题。Apple 对 Bundle ID 是严格区分大小写的,但 Xcode 默认生成小写,坑死人。
3. 证书(Certificates)
你需要两种:
- Development Certificate:用于真机调试
- Distribution Certificate:用于 App Store 发布
生成方式:在 Keychain Access 里“请求证书”,然后上传到 Apple 后台签名。或者用 Xcode 自动管理(推荐新手用)。
但我建议手动搞一次,不然你永远不知道 Code Signing Identity 和 Provisioning Profile 之间那点暧昧关系。
4. Provisioning Profiles(配置文件)
这是把证书、设备、Bundle ID 绑定在一起的“婚约”。分为:
- Development Profile:绑定测试设备 UDID
- App Store Profile:不绑设备,用于发布
关键点:每次改了 Bundle ID、证书或权限(如推送、相机),都要重新生成 Profile 并下载安装到 Xcode!
我们第一次提交就是因为没开 Push Notification 权限,但代码里用了 UNUserNotificationCenter,审核被拒:
“Your app includes API to register for remote notifications but does not include the required ‘aps-environment’ entitlement.”
解决办法:在 Identifiers 里勾选 Push Notifications,重新生成 Distribution Profile,Clean & Build,重新归档。
代码与配置:别让审核员抓到把柄
Apple 审核指南厚得能当枕头,但有几个高频雷区,我列出来,大家自查。
1. Info.plist 必填项
NSCameraUsageDescription:用了相机就得写理由NSPhotoLibraryUsageDescription:访问相册也得说明NSLocationWhenInUseUsageDescription:定位权限同理
别写“为了更好的用户体验”这种废话,审核员会拒。要具体,比如:“用于扫描员工工牌二维码进行打卡”。
2. 能耗优化(别让电池哭)
前面提到的“significant energy”警告,其实是因为我们在后台用了一个定时器每 30 秒轮询服务器。虽然业务需要,但 Apple 不认。
解决方案:
- 改用 Push Notification + Background Fetch
- 或者用 Significant Location Change 触发(如果和位置相关)
我们最后妥协:只在 App 前台时轮询,后台改用静默推送唤醒。虽然体验略降,但过了审。
3. 登录与隐私
如果你的 App 需要登录,必须提供测试账号!且不能是生产环境账号。
我们在审核备注里写了 test / 123456,结果审核员说密码太简单被系统拦截了……最后改成 TestUser2023! 才行。
另外,隐私政策 URL 是强制要求!即使你的 App 不收集任何数据,也得有个页面说明“我们不收集数据”。可以用 GitHub Pages 搞个静态页,几行 Markdown 就行。
打包与上传:Xcode Archive 的玄学时刻
一切准备就绪,终于到了激动人心的 Archive 环节。
步骤:
- Xcode → Product → Scheme → Edit Scheme → Set to Release
- Device 选 Any iOS Device (arm64)
- Product → Archive
这时候,祈祷吧。
常见翻车现场:
- Code Signing Failed:检查 Bundle ID 是否匹配,Profile 是否安装
- Bitcode Enabled:现在 Apple 默认要求关闭 Bitcode(Xcode 14+ 已移除选项),确保 Build Settings 里
Enable Bitcode = No - Missing Icon:App Store 需要 1024x1024 的 App Icon,且不能带圆角、透明背景
我们有一次图标用了 Sketch 导出的 PNG,带了 1px 白边,审核被拒:“Icon contains transparency or rounded corners.” 重做三次才过。
上传成功后,去 App Store Connect 填信息。
App Store Connect:填表填到怀疑人生
这里才是真正的“产品”战场。你以为代码写完就结束了?不,产品经理的噩梦才刚开始。
你需要填:
- App 名称、副标题、描述
- 关键词(最多 100 字符,空格分隔)
- 截图(不同尺寸 iPhone/iPad 各一套)
- 隐私标签(Data Types)
- 审核备注(测试账号、特殊说明)
隐私标签(App Privacy)
这是近年新增的大头。你得如实勾选 App 收集哪些数据:
| 数据类型 | 是否收集 | 用途 |
|---|---|---|
| 联系信息 | 否 | - |
| 位置 | 是 | 员工打卡定位 |
| 用户内容 | 是 | 上传打卡照片 |
| 标识符 | 是 | 设备 ID 用于防刷 |
千万别乱勾“否”!如果代码里用了 CLLocationManager 但这里写“不收集位置”,审核必拒。
我们第一次就漏勾了“标识符”(因为用了 UUID 生成设备 ID),被拒理由:
“Your app’s privacy details do not match its actual data collection practices.”
补上后秒过。
截图规范
- 必须是真实 App 界面,不能是设计稿
- 不能包含 Apple 设备边框(比如 iPhone 图标)
- 不能有“即将上线”、“敬请期待”等未实现功能
我们设计师交来的截图带了 iOS 状态栏(时间 9:41),被拒:“Screenshots must reflect the actual user experience.” 最后用 Simulator 录屏裁剪搞定。
审核等待:心跳加速的 24-72 小时
提交后,进入审核队列。平均 24-48 小时,但我们那次等了 72 小时——因为碰上 Apple 节假日。
期间可以加急(Expedite Review),但理由必须充分,比如“修复严重崩溃”或“法律合规问题”。我们试过写“双11业务急需”,被无情拒绝:“Not a valid reason.”
终于,在第 4 天早上 8 点(我刚送完娃回来),收到邮件:
Congratulations! Your app has been approved.
那一刻,我对着屏幕傻笑,差点把咖啡打翻在机械键盘上。赶紧截图发群里,PM 回了个 🎉,运维兄弟回:“下次上架记得叫上我围观。”
总结:上架不是终点,是新起点
现在,这个 App 已经在 App Store 上线一个月,日活 2000+,零崩溃(靠 Sentry 监控保命)。回头看,整个流程最耗时的不是写代码,而是理解 Apple 的规则并适配它。
对正在准备跳槽的我来说,这次经历也让我在简历上能底气十足地写:“主导完成 iOS App 从 0 到 1 上架,通过率 100%”。下周一面试,HR 问起移动端经验,我就能掏出这套完整流程,而不是支支吾吾说“用过 Flutter”。
顺便说一句,刷题还在继续。今天刚 AC 了一道二叉树层序遍历,感觉离 Offer 又近了一步。
附:App Store 上架 Checklist(自用版)
| 步骤 | 检查项 | 是否完成 |
|---|---|---|
| 账号 | 公司开发者账号已激活 | ✅ |
| Bundle ID | 与 Xcode 一致,无大小写错误 | ✅ |
| 权限 | Info.plist 添加所有 Usage Description | ✅ |
| 证书 | Distribution Certificate 有效 | ✅ |
| Profile | App Store 类型,包含正确 Entitlements | ✅ |
| 图标 | 1024x1024,无透明/圆角 | ✅ |
| 隐私政策 | 有效 URL,内容匹配实际 | ✅ |
| 隐私标签 | App Store Connect 勾选准确 | ✅ |
| 测试账号 | 提供有效测试凭证 | ✅ |
| 审核备注 | 说明特殊逻辑(如后台任务) | ✅ |
最后,给所有还在和 App Store 死磕的战友一句话:你不是一个人在战斗。每一个上架成功的 App 背后,都有一个凌晨三点还在看 rejection email 的程序员。
加油,打工人。等我跳槽成功,请你们喝瑞幸。
(完)

评论 0